Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Native upload to S3 with presigned URL

Been trying with no luck to upload an image to S3 from React Native using pre-signed url. Here is my code:

generate pre-signed url in node:

const s3 = new aws.S3();

const s3Params = {
  Bucket: bucket,
  Key: fileName,
  Expires: 60,
  ContentType: 'image/jpeg',  
  ACL: 'public-read'
};

return s3.getSignedUrl('putObject', s3Params);

here is RN request to S3:

var file = {
  uri: game.pictureToSubmitUri,
  type: 'image/jpeg',
  name: 'image.jpg',
};

const xhr = new XMLHttpRequest();
var body = new FormData();
body.append('file', file);
xhr.open('PUT', signedRequest);
xhr.onreadystatechange = () => {
  if(xhr.readyState === 4){
    if(xhr.status === 200){
      alert('Posted!');
    }
    else{
      alert('Could not upload file.');
   }
 }
};
xhr.send(body);

game.pictureToSubmitUri = assets-library://asset/asset.JPG?id=A282A2C5-31C8-489F-9652-7D3BD5A1FAA4&ext=JPG

signedRequest = https://my-bucket.s3-us-west-1.amazonaws.com/8bd2d4b9-3206-4bff-944d-e06f872d8be3?AWSAccessKeyId=AKIAIOLHQY4GAXN26FOQ&Content-Type=image%2Fjpeg&Expires=1465671117&Signature=bkQIp5lgzuYrt2vyl7rqpCXPcps%3D&x-amz-acl=public-read

Error message:

<Code>SignatureDoesNotMatch</Code>
<Message>
The request signature we calculated does not match the signature you provided. Check your key and signing method.
</Message>

I can successfully curl and image to S3 using the generated url, and I seem to be able to successfully post to requestb.in from RN (however I can only see the raw data on requestb.in so not 100% sure the image is properly there).

Based on all this, I've narrowed my issue down to 1) my image is not correctly uploading period, or 2) somehow the way S3 wants my request is different then how it is coming in.

Any help would be muuuuuucchhhh appreciated!

UPDATE

Can successfully post from RN to S3 if body is just text ({'data': 'foo'}). Perhaps AWS does not like mutliform data? How can I send as just a file in RN???

like image 767
Cole Avatar asked Jun 11 '16 05:06

Cole


People also ask

Does S3 bucket need to be public for PreSigned URL?

By default, all S3 objects are private. Only the object owner has permission to access them. However, the object owner can optionally share objects with others by creating a presigned URL, using their own security credentials, to grant time-limited permission to download the objects.


1 Answers

FormData will create a multipart/form-data request. S3 PUT object needs its request body to be a file.

You just need to send your file in the request body without wrapping it into FormData:

function uploadFile(file, signedRequest, url) {
  const xhr = new XMLHttpRequest();
  xhr.open('PUT', signedRequest);
  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
      if(xhr.status === 200) {
        alert(url);
      } else {
        alert('Could not upload file.');
      }
    }
  };
  xhr.send(file);
};

See https://devcenter.heroku.com/articles/s3-upload-node for example in a browser. Please also ensure your Content-Type header is matched with the signed URL request.

like image 61
Edward Samuel Avatar answered Sep 26 '22 00:09

Edward Samuel