13

I am generating in server side a pre-signed URL request with the following parameters for GeneratePresignedUrlRequest : bucket, key, expiration = in 1 hour and method = PUT.

In my Angular app, I am uploading the file using ng-file-upload

Upload.http({
    url: $scope.signedUrl,
    method: "PUT",
    headers : {
        'Content-Type': $scope.file.type
    },
    data: $scope.file
});

The problem is that I always have a 403 response unless I set the type of the file in GeneratePresignedUrlRequest.contentType.

The problem is that I can't predict in advance what type of file the user will choose (image/png, image/jpeg, text/plain...).

How can I generate a pre-signed url that accept all kinds of content-type ? I tried setting it to null, it keeps sending 403 errors.

Thanks.

c4k
  • 4,270
  • 4
  • 40
  • 65

3 Answers3

7

I just ran into this problem, and just got it working. Replace your Upload.http code with the following:

var reader = new FileReader();
var xhr = new XMLHttpRequest();

xhr.open("PUT", $scope.signedUrl);
reader.onload = function(evt) {
  xhr.send(evt.target.result);
};
reader.readAsArrayBuffer($scope.file);

The problem ends up being that S3 is looking for a specific Content-Type (binary/octet-stream), which it infers when you omit the Content-Type header.

Luke L
  • 83
  • 1
  • 7
3

The value from the Content-Type header is a mandatory component of the signature. It isn't possible to pre-sign a PUT URL without knowing the value that will be sent.

A POST upload is more flexible, since you can allow any Content-Type in the signed policy.

Michael - sqlbot
  • 169,571
  • 25
  • 353
  • 427
  • What do you mean by `POST` upload ? Get the pre-signed URL with `POST`, no content-type and `POST` instead of `PUT` in my Angular app ? – c4k Sep 07 '15 at 22:13
  • It doesn't particularly matter how you get the authorization from your back-end, but S3 has a very different interface for form-based uploads using a `POST` from the browser that seems to be a better match for what you are doing, at least in this one sense: http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html – Michael - sqlbot Sep 07 '15 at 22:26
  • Actually, you might start here: http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-UsingHTTPPOST.html – Michael - sqlbot Sep 07 '15 at 22:28
  • Thanks but I can't use HTML POST form. The form needs to be independant. – c4k Sep 08 '15 at 12:22
  • You technicallyvdon't have to "use" (build, render, or display) a form, you only have to submit the structure of a form back to S3. – Michael - sqlbot Sep 08 '15 at 12:31
  • @Michael-sqlbot do you think my issue is in presigning part or the content uploading part? https://stackoverflow.com/questions/72979158/how-to-set-type-for-uploading-file-to-s3 – János Jul 15 '22 at 07:54
-3

One possible solution might be if you keep track of the extension? eg: ends with ".jpg" -> content type = "image/jpeg", end with ".zip" -> content type = "application/octet-stream".

Ref: get the filename of a fileupload in a document through javascript

Community
  • 1
  • 1
Titi Wangsa bin Damhore
  • 7,101
  • 4
  • 31
  • 36