强制从亚马逊S3服务器下载文件

26
我正在开发一个新的Web应用程序,它依赖于Amazon S3服务器作为存储系统,并使用Codeigniter作为PHP框架。
当链接被点击时,我需要强制文件下载。原始URL看起来像这样:
http://www.our-web.com/download/do/1.jpg
它会生成一个临时签名URL到Amazon S3服务器上的实际文件,像这样:
http://main_bucket.s3.amazonaws.com/post/1/1.jpg?AWSAccessKeyId=AKIAJEOQKYPKC3CCU5RA&Expires=1305395426&Signature=iuzCdA22gImLK192%2BMAhk8OkAY8%3D
我需要使文件在用户点击链接后从真实的Amazon URL开始下载。
我现在有两种方法可以做到这一点:
1. 使用redirect(),这将打开文件而不是下载;或者 2. 修改标题,如下所示的代码:
header('Content-type: application/force-download');
header('Content-Disposition: attachment; filename=' . $file_name);
header('Content-Transfer-Encoding: binary');
header('Expires: 4000');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($generated_file));

readfile($generated_file);

不幸的是,这两种方法都没有帮助我。第二种方法会导致下载文件来自我的网站,而不是直接从亚马逊下载。

我该如何强制文件直接从亚马逊S3服务器下载,而不是从我的网站下载?


使用file_get_contents或cURL来检索S3文件,然后通过头信息进行传递。 - Lawrence Cherone
这就是他用readfile在做什么。 - Lasar
虽然我的确不希望客户通过我的网站下载文件,但肯定有更好的解决方案可以从另一个域名下载。 - Khaled
1
如果您想利用Cloudfront将内容分发给多达19个边缘节点,从您的站点中拉取文件是一个非常糟糕的选择。 - MikeNereson
6个回答

41

您只需要在S3上的文件中设置正确的标头,就可以强制浏览器下载而不是打开文件。请设置以下内容:

Content-Disposition: attachment; filename=FILENAME.EXT
Content-Type: application/octet-stream

上传文件到S3时,您需要设置它们。使用php SDK,您可以使用create_object方法。

或者,您可以在上传后使用'change_content_type'方法,或者通过在S3中将文件复制到自身并设置正确的标头来进行设置。


2
问题已经通过更改文件元数据来解决,代码如下:$opt['meta']['Content-Type'] = 'binary/octet-stream'; - Khaled
2
我相信实际上唯一需要的是内容分配头。不需要设置内容类型。 - dmitrybelyakov
只需将 Content-Type 更改为 application/octet-stream,发送到 S3 即可完成工作!谢谢! - Marcos Mendes

25

已经晚了一步,但通常情况下,对于一个文件,您不想在存储时决定如何使用它。您希望能够仅存储该文件一次,然后在一个区域中可能嵌入文件或在浏览器中显示,在另一个区域中让用户下载同一文件。

幸运的是,您可以通过在请求URL中提供重写参数来实现此目的。这仅适用于签名请求,但幸运的是,您已经在这样做了。

如果您添加一个类似于&request-content-type="application/force-download"的参数,那么应该就可以了。

请查看S3 GET对象文档的请求参数部分: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html


8
我认为正确的请求参数是request-content-disposition=attachment; filename=FILENAME.EXT",你的request-content-type起作用了,因为它是未知的内容类型,所以浏览器默认下载它。 - Seth
太棒了,是我想要的。我宁愿不要在存储时决定这个(特别是因为已经有成千上万个文件在S3中,我不想修改)。 - Simon East
5
截至2019年,实际上是 response-content-disposition=attachment;%20FILENAME.EXT - Luke
2
这里是Luke的评论文档,链接如下:https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html#API_GetObject_Example_9 - Gerbus
这对我有用,添加预签名请求:https://dev59.com/vYbca4cB1Zd3GeqPSS3E#44508783 - Fer Torrs

11

这个问题是关于如何通过编程的方式进行设置,但手动通过AWS控制台进行设置的步骤如下:

  • 在S3中选择一个文件。
  • 属性 > 元数据 > 添加更多元数据
  • 键: Content-Disposition 值: Attachment
  • 保存

非常有用,因为这个答案在谷歌搜索中排名很高,我需要向一个非技术人员展示如何在AWS上完成这个操作。 - Nick

0
如果您的S3网站在CloudFront后面,请不要忘记在再次尝试之前使文件失效!这将清除缓存的文件。
您可以通过访问以下页面来到达无效页面:
Cloudfront -> Distribution Settings -> Invalidation

然后输入文件路径。

使文件失效需要一些时间。对于我的文件,它花费了5分钟。


0

你应该使用$s3->getObject()ResponseContentDisposition参数来设置响应的内容描述,这可以用于强制下载文件。

$s3->getObject([
  "Bucket" => "example",
  "Key" => "example.txt",
  "ResponseContentDisposition" => 'attachment; filename="example.txt"'
]);

-5

你无法告诉浏览器如何处理远程文件。通过重定向到亚马逊,你告诉浏览器在那里启动一个新的请求。你对该请求没有任何控制权。

我能想到的唯一解决方案是将图片打包成zip文件或类似格式。当然,这会增加另一种(可能很烦人的)复杂性。


图片只是一个例子,可能我忘了提到几种文件类型特别是pdf和docx。无论如何,感谢您的回复,但我认为有一种方法可以解决这个问题。实际上,许多大型网站使用外部存储系统。 - Khaled
1
我不知道在上传文件到 S3 时可以设置标题的可能性。知道这点很好。 - Lasar
我认为亚马逊可能有更好的解决方案,但我们选择使用亚马逊S3是因为它可以直接从浏览器使用POST进行上传。无论如何,感谢您的帮助。 - Khaled

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接