0

I'm building an app in NodeJS that stores files in Amazon S3 using the Knox S3 client. Everything works well for uploading files, moving files around, etc.

Now I want to use the Query String Authentication mechanism to allow direct downloads of the files. To do this, I have some code on my NodeJS server call to the Knox library and create a signed url.

The code looks like this:


exports.getS3Policy = function(file) {
  var date = moment().add("min", 60).toDate();
  var expires = new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(),  date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());

  return knoxClient.signedUrl(file, expires);
};

This code returns a proper URL with the authentication parameters. For example:

https://my-bucket.s3.amazonaws.com/some/folder/file.ext?Expires=1234567890&AWSAccessKeyId=ABCDEFGHIJKLMNO&Signature=someEncodedSignature

According to all of the documents I've read, this is a proper URL. I'm not getting any errors from Amazon with this url. The expiration is correct (I can verify this by creating an expiration of 1 second and then getting an expired error). The file path is correct, as well.

When I hit the url in my browser, though, my browser (latest Chrome on OSX) cancels the download of the file, even though I'm getting a 200 ok response with the right file information.

Here is a copy of the request info from Chrome dev tools (sensitive bits replaced):


Request URL:https://my-bucket.s3.amazonaws.com/some/folder/file.ext?Expires=1234567890&AWSAccessKeyId=ABCDEFGHIJKLMNO&Signature=someEncodedSignature
Request Method:GET
Status Code:200 OK
Request Headers
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:no-cache
Connection:keep-alive
DNT:1
Host:my-bucket.s3.amazonaws.com
Pragma:no-cache
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.65 Safari/537.36
Query String Parameters
Expires:1234567890
AWSAccessKeyId:ABCDEFGHIJKLMNO
Signature:someEncodedSignature
Response Headers
Accept-Ranges:bytes
Content-Length:341390
Content-Type:application/octet-stream
Date:Tue, 10 Sep 2013 13:22:55 GMT
ETag:"fc4d24e752097f212e111f2736af7162"
Last-Modified:Tue, 10 Sep 2013 01:40:31 GMT
Server:AmazonS3
x-amz-id-2:some-id
x-amz-request-id:some-request-id

As you can see, the server response is "200 ok". The content-length of 341390 is also the correct lenght of the file I'm attempting to download - this is the actual file size. I'm getting the content type as "application/octet-stream" because that's how I told S3 to store the files... I just want the raw download, basically.

But after getting this response from S3, Chrome cancels the download. Here's a screencap from devtools, again:

FireFox and Safari both download the file as expected. Why is chrome canceling the download? What am I doing wrong? Is it the content type? or ?

Community
  • 1
  • 1
Derick Bailey
  • 72,004
  • 22
  • 206
  • 219

1 Answers1

1

Of course I find the answer as soon as I post the question... it's a bug in Chrome

https://code.google.com/p/chromium/issues/detail?id=104331

The fix will be available starting with Chrome/Chromium 30.x. Please open a new issue if you are seeing similar issues with versions of Chrome 30 or above.

The supported means of indicating that a resource must be downloaded is to use the Content-Disposition header field (https://www.rfc-editor.org/rfc/rfc6266).

Looks like I have to get S3 to set a content-disposition in the response.

Community
  • 1
  • 1
Derick Bailey
  • 72,004
  • 22
  • 206
  • 219