Client-side S3 file(s) uploading

We'll see how to upload a static file on Amazon S3 without consuming server bandwidth.

This can be quite useful in a serverless architecture, to prevent dealing with the AWS API Gateway's weird support of binary files. Or in general, to decrease your server loads and increase uploading speed.

It works by asking our server to get a signed upload URL from S3 and then uploading the file(s) directly to it.

AWS side

Create a bucket on S3 and give it public read rights.

Replace the bucket's CORS configuration with the following:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
	<AllowedOrigin>*</AllowedOrigin>
	<AllowedMethod>GET</AllowedMethod>
	<AllowedMethod>POST</AllowedMethod>
	<AllowedMethod>PUT</AllowedMethod>
	<MaxAgeSeconds>3000</MaxAgeSeconds>
	<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

Server side

const AWS = require('aws-sdk')

let s3 = new AWS.S3()

app.post('/s3uploadurl', (req, res, next) => {

	let s3Params = {
		Bucket: 'my-dope-bucket',
		Key: req.body.name,
		ContentType: req.body.type,
		ACL: 'public-read'
	}

	let uploadURL = s3.getSignedUrl('putObject', s3Params);

	res.send({
		uploadURL: uploadURL
	})
})

Client side

In our example we use JQuery, but everything is easily replaceable with pure JS (using fetch for ajax call)

<form action='/whatever'>
	<!-- File selector, once we select files, they'll be automatically uploaded to s3 -->
	<input type="file"/>

	<!-- myFile will contain the URL of the file hosted on S3 -->
	<input type="hidden" name="myFile" value="">
</form>

<script>
let s3baseUrl = 'https://s3.eu-west-3.amazonaws.com/my-dope-bucket/' // CHANGE WITH YOUR OWN BUCKET AND REGION

let inputElement = $("form input[type='file']")[0]
inputElement.onchange = function(event) {
	let file = inputElement.files[0]

	let reader = new FileReader()
	reader.addEventListener('loadend', function(e){
		// $('.spinner').show() // Optional, if you have a spinner, now is a good time to show it

		$.ajax({
			type: 'POST',
			url: "/s3uploadurl",
			data: {
				name: file.name,
				type: file.type
			},
			success: function(result) {
				$.ajax({
					url: result.uploadURL,
					type: 'PUT',
					data: new Blob([reader.result], {type: file.type}),
					processData: false,
					contentType: false,
					success: function(result) {
						// $('.spinner').hide() // Hide the spinner
					
						$('[name="myFile"]').val(s3baseUrl + file.name)
					}
				})
			}
		})
	})

	reader.readAsArrayBuffer(file)
}
</script>

I can let you know when I write new posts