React Native upload to S3 with presigned URL
Solution 1
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.
Solution 2
I've wasted way too much time on uploading to pre-signed S3 URL on both iOS and Android. What worked for me was rn-fetch-blob lib
Code snippet:
import RNFetchBlob from 'rn-fetch-blob'
const preSignedURL = 'pre-signed url'
const pathToImage = '/path/to/image.jpg' // without file:// scheme at the beginning
const headers = {}
RNFetchBlob.fetch('PUT', preSignedURL, headers, RNFetchBlob.wrap(pathToImage))
Solution 3
"rn-fetch-blob": 0.12.0,
"react-native": 0.61.5
This code works for both Android & iOS
const response = await RNFetchBlob.fetch(
'PUT',
presignedUrl,
{
'Content-Type': undefined
},
RNFetchBlob.wrap(file.path.replace('file://', '')),
)
Note
{'Content-Type': undefined}
is needed for iOS
Cole
Updated on July 08, 2022Comments
-
Cole almost 2 years
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???
-
Inchoon Park over 7 yearsCan you elaborate on ensuring that the Content-Type header is matched with the signed URL request? I do not quite understand what you mean by this.
-
Abner Terribili almost 7 yearsYou just need to set
xhr.setRequestHeader('Content-Type', fileType)
-
Marklar almost 7 years
-
leomayleomay almost 6 years@edward-samuel, I am wondering how to achieve the same with Fetch instead of XMLHttpRequest
-
Roy Lee over 5 yearsyou're my life saver!
-
Xavi A. almost 5 yearsThis code worked for me on iOS, but not on Android. I get xhr.status = 0 but nothing is uploaded to S3.
-
Umang Loriya about 4 years@Peter Machowski Is it working normally for large files too?
-
Peter Machowski about 4 years@UmangLoriya I haven't tested it with files other than <10Mb images but I don't see a reason for not working with larger files.
-
Mayoul almost 4 yearsCan you explain how's your headers object looks like ? I'm passing a FormData object containing all the needed infos with the presigned URL (bucket, region, key, ...), but then I'm getting a black screen when attempting to upload...
-
Peter Machowski over 3 years@Mayoul there are no specific headers needed. Black screen suggest error in some other code.
-
highjump about 3 yearsI tried this, but it crashes on iOS, have you experienced this?
-
sj_959 almost 3 years@PeterMachowski Thanks a lot! The file gets uploaded but when I try to download it, it says No Such Key. What could cause this ?