8

I'm trying to upload a file to Amazon S3 using a PUT request. I generate my signed urls using boto. If I do

curl --upload-file test.jpg $SIGNED_URL

then the upload works fine (so there's not an issue with the signed urls). I've also set the referer to various things with curl and it still works, so I don't think there is a CORS issue either. I'm trying to upload a file blob using javascript as follows:

var xmlhttp = new XMLHttpRequest();    
xmlhttp.open("PUT", $SIGNED_URL);
xmlhttp.setRequestHeader('Content-Type', blob.type);
xmlhttp.send(blob);

This always returns a 403 Forbidden error. I'm finding it surprisingly difficult to find any information online about PUTing data to S3 from javascript. I realize now that it's more common to POST form data, but is there any way to modify what I'm doing to also work?

Edit: I've found the body of the error now:

> <Error><Code>SignatureDoesNotMatch</Code> <Message>The request
> signature we calculated does not match the signature you provided.
> Check your key and signing method.</Message>

So it appears that the signing is the issue. Again, the same exact url works when I use it with curl (even after it's failed in javascript).

Ivanna
  • 1,197
  • 1
  • 12
  • 22

2 Answers2

8

The issue was the 'Content-Header' which curl was not sending. Chrome sends this header regardless of whether you add it explicitly. To get a signed url that will work with this header you need to pass headers={'Content-Type':type} to generate_url in boto.

Ivanna
  • 1,197
  • 1
  • 12
  • 22
0

For me the problem was I was not setting the content-type while generating presigned URL, so added that while generating presigned url

java.util.Date expiration = new java.util.Date();
        long msec = expiration.getTime();
        msec +=  60 * 60 * 1000; // 1 hour.
        expiration.setTime(msec);

        GeneratePresignedUrlRequest generatePresignedUrlRequest =
                new GeneratePresignedUrlRequest("bucket_name", "someRandomKey");
        generatePresignedUrlRequest.setMethod(HttpMethod.PUT); // Default.
        generatePresignedUrlRequest.setExpiration(expiration);
        generatePresignedUrlRequest.setContentType("application/octet-stream");

        URL s = s3Client.generatePresignedUrl(generatePresignedUrlRequest);
        System.out.println("Secure PUT URL is "+s);

And here is the curl

curl -v -X PUT \
  'generated_secure_url' \ 
  -H 'Content-Type:application/octet-stream' \
  --data-binary '@swiggy-hackathon.jpg'
Mahtab Alam
  • 1,810
  • 3
  • 23
  • 40