Upload Images to S3 buckets with React and Node in a scalable way, using getSignedUrl

wassim nassour
3 min readApr 14, 2023

--

08/04/2023 · Wassim Nassour

Hi folks, we will discuss how to upload images to S3 buckets using the getSignedUrl method. This method enables the direct uploading of images to S3 without having to first upload them to your server.

How it works and why this way is more scalable:

full flow

One of the ways to upload images to S3 buckets is to handle the image on the server before sending it to S3. However, this requires more resources on the backend, which can result in slower processing times. An alternative approach is to upload the image with a signed URL. This method requires fewer resources because we only need to specify the operation name and some parameter objects.

After using the method to upload images to S3 buckets on the server, we are returned a URL as result. We can utilize the URL to perform a PUT request with the image on the React client. This approach enables direct uploading of the image to S3 without passing through the server. Consequently, the approach is more scalable, as it reduces the backend’s load and improves processing times.

This image explains the full flow.

Security :

  • Users cannot spam our S3 bucket with many files.
  • a URL for one file and then upload a different one.
  • URLs are generated securely through a request between our server and AWS.
  • URLs can only work for the specific S3 bucket they were created for.

implementations :

const AWS = require('aws-sdk')

const s3 = new AWS.S3()
AWS.config.update({
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
})
app.post('/upload-image', async (req, res) => {
const { fileName, fileType } = req.query
const s3Params = {
// name_of_your_bucket_here
Bucket: process.env.S3_BUCKET,
// define name of the resource in the s3 bucket it's better to use it like this
// way resourseName +'/'+ random id better to use UID
Key: fileName,
// file type Image/Jpeg...
ContentType: fileType,
// Expiration Date of the url
Expires: 60,
}
try {
const signedUrl = await getSignedUrl(s3, s3Params)
console.log(signedUrl)
res.json({ signedUrl })
} catch (err) {
console.error(err)
next(err)
}
})

React Implementation:

Let’s consider that you have created a form and are now in the function that will upload an image to your backend.

async function uploadImage({ fileName, fileType }) {
const options = { fileName, fileType }
const response = await axios.post('/upload-image', options)
if (response?.url) { axios.put(response?.url, file, options) }
}

Handling CORS

  • S3 Bucket setting
  • Create a DNS compliant bucket name
  • Set default encryption
  • Give appropriate read and write permissions
  • Add the following CORS rules
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>/ origins allowed to upload image /</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>HEAD</AllowedMethod>
<AllowedMethod>DELETE</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

full code

Originally published at https://wnassour.com.

--

--

wassim nassour
wassim nassour

Written by wassim nassour

React ⚛️ | Node.js 📦 | Golang 🐿 (Open for new opportunities)