How to Upload Files to Amazon S3 Using Pre-signed URLs

If uploading files to Amazon S3, a preferred method is to use pre-signed URLs. Pre-signed URLs are URLs that have been signed using AWS credentials. They give applications or users temporary permission to upload files to that URL. This is safer because the application or user uploading files will never see any AWS credentials.

In this post, I’ll show you how to upload files to Amazon S3 using pre-signed URLs from the web and from React Native.

But, I’m not going to go into the specifics of creating pre-signed URLs. There are libraries in most languages to help with their creation. You may also decide to implement it yourself. Here, I’m presuming that you have the ability to generate pre-signed URLs and that you can send requests to a server to receive a URL as a response.

It’s time to upload images!

Upload to Amazon S3 From the Web

Our first example will be from the web. Let’s say you have a form where users can upload avatar images. When the user selects an image, you want to start the process of uploading an image.

function makeS3Request(data, file) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()

    xhr.onreadystatechange = () => {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          resolve(data.url)
        } else {
          reject({
            status: xhr.status,
            statusText: xhr.statusText,
          })
        }
      }
    }

    xhr.open('PUT', data.signed_request)
    xhr.send(file)
  })
}

async function uploadFileToS3(file, urlElement, imagePreviewElement) {
  try {
    const response = await fetch(
      `/uploads/sign?file_name=${file.name}&file_type=${file.type}`
    )
    const {data} = await response.json()
    const url = await makeS3Request(data, file)

    if (urlElement) {
      // Save image to hidden field
      urlElement.value = url
    }

    if (imagePreviewElement) {
      // Preview Image
      imagePreviewElement.src = url
    }
  } catch (e) {
    alert('Could not upload file.')
  }
}

const imageUploadElement = document.getElementById('avatar_uploader')

if (imageUploadElement) {
  // Let's set up an event handler on the file handler
  imageUploadElement.onchange = () => {
    const file = imageUploadElement.files[0]

    if (file === null) {
      alert('No file selected.')
    } else {
      // function to handle S3 uploading
      uploadFileToS3(
        file,
        document.getElementById('avatar_url'),
        document.getElementById('avatar_preview')
      )
    }
  }
}

Upload to Amazon S3 From React Native

Let’s now do the same from React Native.

function makeS3Request(data, file) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()

    xhr.onreadystatechange = () => {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          resolve(data.url)
        } else {
          reject({
            status: xhr.status,
            statusText: xhr.statusText,
          })
        }
      }
    }

    xhr.open('PUT', data.signed_request)
    xhr.setRequestHeader('Content-Type', file.type)
    xhr.send({
      uri: file.uri,
      type: data.file.type,
      name: data.file.name,
    })
  })
}

async function upload(file) {
  const response = await fetch(`https://example.com/uploads/sign?file_name=${file.name}&file_type=${file.type}`)

    const {data} = await response.json()
    return await makeS3Request(data, file)
}

In both examples, XMLHttpRequest is used. The code between the web and React Native examples only slightly differ.

Happy Uploading!

At Revelry, our team is focused on shipping great software every day.

Get to know some of the team!
We’re transparent about how we work. For a look at how we build digital products, check out
Lean Agile Processes and Tools.
Would you like to create with us? Check out our Open Source projects and careers.

Keep in touch by subscribing to CODING CREATIVITY.