1
votes

I'm trying to upload files from the browser (Chrome version 41.0) to S3 using a presigned URL with the PUT method. I'm generating the presigned URL on my server correctly, as it works fine from CURL:

curl -X PUT "Content-Type:" --data "junk" "https://mypresignedurl"

I'm now trying to upload from the browser using jQuery:

$.ajax({ url: "https://mypresignedurl", method: 'PUT', // I've also tried type: 'PUT' data: "junk", })

This is refused by AWS with a SignatureDoesNotMatch code. The error specifies:

<StringToSign>GET 1427216999 /mybucket/myfile</StringToSign>

But I wasn't using GET - I was using PUT. If I check the network tab of Chrome, it correctly lists the request as a PUT. So why does AWS interpret browser PUT requests as GETs and reject them?

(This does not seem to be CORS-related. I've set CORS on my bucket, and then tested from CURL using -H "Origin: http://elsewhere.com". I've also tried passing "type" instead of "method". I've tried using POST instead of PUT - even then AWS still lists the StringToSign as a GET. I've tried various contentTypes. FWIW, my presignedURL upload logic all works fine from .NET in a WPF app.)

1

1 Answers

2
votes

Ok, got it working. I can now upload from a browser using presigned URLs.

The thing that threw me off was the issue of S3 interpreting my PUTs as GETs. But this is actually wrong. To figure out the permissions problem I'd looked in Chrome's network tab. Before the PUT request to S3 there was an OPTIONS request to the same S3 endpoint. I clicked on this to get more info, and that's when I saw the SignatureDoesNotMatch message with <StringToSign>GET ... </StringToSign> But clicking on this link in the browser caused the browser to try to GET the resource from S3. And that's not what you're supposed to do with it. The request is being retrieved with the OPTIONS method. You can do this manually with curl -X OPTIONS ... if you really want to, and then it correctly lists how this url may be accessed. But don't click on it in the browser, or the browser will try to GET it, yielding a nonsense message that's got nothing to do with your original PUT request.

So why was I getting an error with uploading in the first place? It turns out I was leaving content-type blank. The browser apparently doesn't like this and turns it into " , text/plain ...". It seems curl and .net, on the other hand, are ok with a blank content-type, hence the discrepancy in their behaviors. When I used the same content-type = "text/plain" both in generating the presigned URL on the server and in the ajax upload, everything worked.