Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Direct uploading to AWS S3 : SignatureDoesNotMatch only for IE

I use Amazon Web Service S3 to upload and store my files. I generate a pre signed url with AWS Sdk for Node.js server-side to upload directly files from browser thanks to this pre signed url.

How it works

Server-side I have a method wich returns the pre-signed url.

AWS.config.loadFromPath(__dirname + '/../properties/aws-config.json');
AWS.config.region = 'eu-west-1';
//Credentials are loaded

var s3 = new AWS.S3();
var docId = req.query.doc;
var params = {
    Bucket: res.locals.user.bucketId, 
    Key: docId+"."+req.query.fileExtension, 
    ACL : "bucket-owner-read", 
    ContentType : req.query.fileType
};
s3.getSignedUrl('putObject', params, function (err, url) {
    if(url){
        res.writeHead(200);
        var result = {
            AWSUrl : url
        };
        //Generates pre signed URL with signature param
        res.end(JSON.stringify(result));
    }
}

I upload directly my file to S3 client-side

var loadToAWSS3 = function(url, file, inputFileId){
    var data = new FormData();
    data.append('file', file);

    $.ajax({
        url: url,//url getted from server-side method
        type: 'PUT',
        data : data,
        headers: { 
            'Content-Type': file.type
        },
        processData: false,
        xhr: function() {
            var myXhr = $.ajaxSettings.xhr();
            if(myXhr.upload){
                myXhr.upload.addEventListener('progress',function(e){
                    if(e.lengthComputable){
                        var max = e.total;
                        var current = e.loaded;

                        var percentage = (current * 100)/max;
                        //stuff to handle progress...
                    }  
                }, 
                false);
            }
            return myXhr;
    },
    statusCode: {
        200: function () {          
            //some stuff 
        }
    });
}

Chrome & Firefox behaviors

Works as expected, the pre signed url is getted, then the file is uploaded, I can see it in my AWS S3 console.

Lovely IE 11

SignatureDoesNotMatch error ! IE add some extra stuff to Content-Type request header not expected by AWS which causes error in the signature comparison. Server-side, the Sdk generates signature based on :

ContentType : req.query.fileType //(something like application/pdf)

whereas when I inspect the request with IE debugger, I see

 Content-Type   application/pdf, multipart/form-data; boundary=---------------------------7df2f3283091c

in Chrome my request header is fine

Content-Type: application/pdf

What can I do to remove this IE extra Content-Type ? If not possible, how can I generate this extra stuff before sending the request in order to get the pre-signed url with the extra stuff in the signature ?

like image 922
ValentinV Avatar asked Dec 24 '22 21:12

ValentinV


1 Answers

OK, I finally figured it out.

Using FormData() simulates that you are sending files through a form. That's why IE always adds

multipart/form-data; boundary=---------------------------7**********

To get around the problem I use raw Javascript with XMLHttpRequest thanks to this answer

var xmlHttpRequest = new XMLHttpRequest();

xmlHttpRequest.open('PUT', url, true);
xmlHttpRequest.setRequestHeader('Content-Type', file.type);
xmlHttpRequest.send(file);

And it works with Chrome, Firefox, IE 11 (I have not tested with IE<11 but according to W3Schools it works for IE7+). No more extra content type with IE.

Hope this helps

like image 77
ValentinV Avatar answered Jan 13 '23 11:01

ValentinV