taiki-t's diary

React Native, Rails そして雑多な記録: The world is waiting for you to give it a meaning.

Presigned post to aws s3 from React Native

It took a longtime to figure out how to upload images to aws s3 using presigned post from React Native.

The key part is to use FormData to construct a payload and post it with POST http method. PUTing FormData payload with pre-signed URL didn’t work for me at least.

Server Side

I use Ruby on Rails for server side here. Read Direct to S3 Image Uploads in Rails | Heroku Dev Center to fully understand the code below.

FYI: You don’t need to setup CORS settings in the article for native apps.

The security model for XMLHttpRequest is different than on web as there is no concept of CORS in native apps. - https://facebook.github.io/react-native/docs/network.html

Here is the code:

# In your controller
def presigned_url
  options = {
    key: "uploads/photos/#{SecureRandom.uuid}/${filename}",
    success_action_status: '201',
    acl: 'public-read'
  }
  presigned_post = S3_BUCKET.presigned_post(options)

  # FIXME: http://stackoverflow.com/a/36941996/2930161
  if presigned_post
    render plain: { presignedPost: { fields: presigned_post.fields, url: presigned_post.url } }.to_json, status: 200, content_type: 'application/json'
  else
    render plain: { error: 'No presigned urls.' }.to_json, status: 422, content_type: 'application/json'
  end
end

React Native

// In your component.
async _uploadToS3() {
  try {
    // implement your method to fetch a presigned post object.
    const presignedPost = await this._fetchPreSignedPost()

    let formData = new FormData();

    Object.keys(presignedPost.fields).forEach((key) => {
      formData.append(key, presignedPost.fields[key]);
    });

    formData.append('file', {
      // Provide an url of a target image.
      uri: this.image.uri,
      type: 'image/jpeg',
      name: 'image.jpg',
    })

    const presignedUrl = presignedPost.url

    fetch(presignedUrl, { method: 'POST', body: formData, headers: { 'Content-Type': 'multipart/form-data'} })
  }
  catch (error) {
    console.log(error)
  }
}

This Answer on StackOverflow inspired me for the code, thanks:

stackoverflow.com

Other Links

http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Bucket.html#presigned_post-instance_method

http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/PresignedPost.html

Upload an Object Using a Pre-Signed URL (AWS SDK for Ruby) - Amazon Simple Storage Service

react-native/XHRExampleFormData.js at master · facebook/react-native · GitHub