React Native upload to S3 with presigned URL

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.


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))

"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