Nishchal Gautam Nishchal Gautam - 8 months ago 62
Javascript Question

Upload images to S3 doesn't work

I'm trying to upload files to S3 without having to send to my server. I've a endpoint which gives me signed S3 URL where I can make

requests to store files to my bucket.

I tried to do couple of things on JavaScript side which didn't work. (I'm not using amazon's SDK, and prefer not to, because I'm looking for simple file upload and nothing more than that)

Here's what I'm trying to do currently in JavaScript:

uploadToS3 = () => {
let file = this.state.files[0];
let formData = new FormData();
formData.append('Content-Type', file.type);
formData.append('file', file);
let xhr = new XMLHttpRequest();'put', this.signed_url, true);

I tried bunch of options, I prefer using fetch because I don't really care for upload progress since these are just images. I used xhr code from somewhere to try out like above. These do make network calls and seem like they should work but they don't.

Here's what happens: An object is created on S3, when I go to public URL, they get downloaded and when I use image viewer to open them, they say it's not valid JPG.

I'm thinking I'm not doing the upload correctly.

Here's how I do in postman:

Postman with file chosen

Notice I have correct signed URL and I've attached binary image file to the request. And added a header stating content type is image/jpeg as shown below:

Specified headers

When I login to S3 and go to my bucket, I can see an image and I can go to it's public URL and view in browser. This works perfect and is exactly what I want, now I don't know how I could achieve the same on JavaScript.

PS: I even tried to click on
on postman, it doesn't generate file code for me.

Answer Source

The problem here starts with xhr.send(formData).

When you PUT a file in S3 you don't use any form structures at all, you just send the raw object bytes in the request body.

Content-Type: and other metadata goes in the request headers, not in form data in the body.

In this case, if you download your uploaded file and view it with a text editor, the problem should be very apparent once you see what your code is actually sending to S3, which S3 then obediently stores and serves up on subsequent requests.

Note that S3 does have support for browser-based form POST uploads, but when doing so the signing process is significantly different, requiring you to create and sign a policy document, so that you can send the form, including the policy and signature, to the browser and allow an otherwise-untrusted user to upload a file -- the signed policy statement prevents the browser user from tampering with the form and performing actions that you didn't intend.