14

I'm trying to upload images to aws-s3 via a signed-url from NodeJS server (not from a browser). The image to upload has been generated by NodeJS. I'm getting the signed-url from aws and succeeding to upload it to s3.

But my image is corrupted. For some reason, S3 is adding some headers to my image (compare image attached).

What am I doing wrong?

getting the signed url:

    try {
        var params = {
            Bucket: bucketName,
            Key: 'FILE_NAME.png',
            Expires: 60
        };
        const url = await s3.getSignedUrlPromise('putObject', params);
        return url;
    } catch (err) {
        throw err;
    }

uploading to s3

        var stats = fs.statSync(filePath);
        var fileSizeInBytes = stats["size"];
        const imageBuffer = fs.readFileSync(filePath);

        var formData = {
            'file': {
                value: imageBuffer,
                options: {
                    filename: 'FILE_NAME.png'
                }
            }
        };

        request({ 
            method: 'put',
            url, 
            headers: {
                'Content-Length': fileSizeInBytes,
                'Content-MD': md5(imageBuffer)           
            }, 
            formData 
        }, function (err, res, body) {
             console.log('body',body);
        });

Compare between the actual image and the uploaded image to s3. S3 added some headers:

compare

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Nir Krevner
  • 241
  • 3
  • 6

4 Answers4

33

I know this is old but I struggled with the same issue for a while. When uploading using a pre-sgined url, DO NOT use new FormData();

One thing I noticed that all of my files on s3 were exactly 2kb larger than the originals.

<input type="file" id="upload"/>

var upload = document.getElementById('upload');
var file = upload.files[0];

//COMMENTED OUT BECAUSE IT WAS CAUSING THE ISSUE
//const formData = new FormData();
//formData.append("file", file);

// Assuming axios

const config = {
    onUploadProgress: function(progressEvent) {
        var percentCompleted = Math.round(
            (progressEvent.loaded * 100) / progressEvent.total
        );
        console.log(percentCompleted);
    },
    header: {
        'Content-Type': file.type
    }
};

axios.put(S3SignedPutURL, file, config)
   .then(async res => {
        callback({res, key})
    })
    .catch(err => {
        console.log(err);
    })
Sam Munroe
  • 1,266
  • 2
  • 20
  • 41
3

Came here in 2023, was facing the same problem using formdata, but in postman, before handing it to the front end department.

To handle it in postman, use the type of request body as binary: enter image description here

And don't forget to add the proper headers.

Huthaifa Muayyad
  • 11,321
  • 3
  • 17
  • 49
1

I followed the above solution for react js

What I was doing before uploading an image is passing through the createObject URL and then passing it to the API body.

if (e.target.files && e.target.files[0]) {
            let img = e.target.files[0];
            **setImage(URL.createObjectURL(img))**

Correct Way:

if (e.target.files && e.target.files[0]) {
            let img = e.target.files[0];
            **setImage(img)**

Work For me, Thanks Sam Munroe

-1

Try to specify the content type in the request as Content-Type multipart/form-data.

szaboat
  • 593
  • 3
  • 11