【问题标题】:Amazon S3 Change file download nameAmazon S3 更改文件下载名称
【发布时间】:2011-02-06 08:58:31
【问题描述】:

我将文件存储在 S3 上,并以 GUID 作为键名。

我正在使用预签名的 URL 来按照 S3 REST API 进行下载

我将原始文件名存储在自己的数据库中。当用户点击从我的网络应用程序下载文件时我想返回他们的原始文件名,但目前他们得到的只是一个 GUID。我怎样才能做到这一点?

我的网络应用程序在 salesforce 中,因此我没有太多控制权来执行响应。将所有下载文件重定向到网络服务器,然后由于调控器限制重命名它。

我可以使用一些 HTML 重定向、元刷新、Javascript 吗?有什么方法可以更改 S3 的下载文件名(我唯一能想到的就是将对象改成新名称,下载它,然后删除它)。

我想避免为每个用户创建一个存储桶,因为我们会有很多用户,但仍然不能保证每个存储桶中的每个文件都有一个唯一的名称

还有其他解决方案吗?

【问题讨论】:

  • 另一个因离题而关闭的合法 SO 问题!问题和答案也比绝大多数常见问题/答案拥有更多的选票......
  • 是的,显然更重要的是,我们尝试随意地组织事物,因为在未来甚至现在都不能保证有意义。当人们只是愉快地询问和回答与软件行业相关的问题时会发生什么?谁真正因为这个组织找到了答案?谁会发现软件中的任何组织结构在整个行业中都是一致且明确定义的?如果没有这个需要花费大量时间组织的明确定义,那就是相当愚蠢的浪费时间......
  • 我找到了这个答案,我对这个组织没有意见。

标签: rest amazon-s3 amazon-web-services


【解决方案1】:

我猜你的十字架把这个问题发到了Amazon S3 forum,但为了其他人,我想在这里发布答案:

如果每个 S3 对象只有一个“用户文件名”,那么您可以在 s3 文件上设置 Content-Disposition 标头来设置下载文件名:

Content-Disposition: attachment; filename="foo.bar"

为了公平起见,我想提一下,在亚马逊论坛上提供正确答案的不是我,所有的功劳都应该归于 Colin Rhodes ;-)

【讨论】:

  • 看看下面的答案,自 2011 年 1 月起,也可以在每个 GET 请求的基础上进行。因此,您可以拥有任意数量的“用户文件名”。
  • 我需要在文件名中添加引号以使其正常工作,所以我的标题是: Content-Disposition: attachment;文件名="foo.bar"
  • 在 c# 中:request.ResponseHeaderOverrides.ContentDisposition = "attachment; filename=foo.bar";
  • 您可以即时覆盖响应标头:https://...example.txt?response-content-disposition=attachment;filename=foo.bar
  • @alaster 这对你有用吗?我收到The request signature we calculated does not match the signature you provided. Check your key and signing method. 当我删除此response-content-disposition=attachment;filename=foo.bar 时,它会下载文件但使用原始名称。
【解决方案2】:

2011 年 1 月上旬,S3 添加了请求标头覆盖。此功能允许您“动态”更改单个请求的 Content-Disposition 标头。

有关详细信息,请参阅 S3 documentation on getting objects

【讨论】:

    【解决方案3】:

    虽然接受的答案是正确的,但我发现它非常抽象且难以使用。

    这是解决上述问题的一段 node.js 代码。我建议将其作为 AWS Lambda 执行以生成预签名的 Url。

    var AWS = require('aws-sdk');
    var s3 = new AWS.S3({
        signatureVersion: 'v4'
    });
    const s3Url = process.env.BUCKET;
    
    module.exports.main = (event, context, callback) => {
    var s3key = event.s3key
    var originalFilename = event.originalFilename
    
    var url = s3.getSignedUrl('getObject', {
            Bucket: s3Url,
            Key: s3key,
            Expires: 600,
            ResponseContentDisposition: 'attachment; filename ="' + originalFilename + '"'
        });
    
    [... rest of Lambda stuff...]
    
    }
    

    请注意传递给s3.getSignedUrl函数的params对象的ResponseContentDisposition属性。

    更多信息请参阅http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#getObject-propertygetObject 函数文档

    【讨论】:

    • 可能不是很明显,我会尽量解释得更好。这个想法是我将上传文件的名称列表保存在数据库中,然后当有人想要下载它时,我使用 originalFilename 作为参数执行 lambda。
    • 标头值无法使用 ISO-8859-1 表示。
    • 应该对原始文件名进行编码。我使用 encodeURIComponent 来解决错误
    【解决方案4】:

    我有同样的问题,我通过在将文件提交到 S3 时设置 http 标头“content-disposition”解决了这个问题,SDK 版本是 AWS SDK for PHP 3.x。这是文档http://docs.amazonaws.cn/en_us/aws-sdk-php/latest/api-s3-2006-03-01.html#putobject

    我的一段代码

        public function __construct($config) 
        {
            $this->handle = new S3Client([
                'credentials' => array(
                    'key' => $config['key'],
                    'secret' => $config['secret'],
                ),
                ...
            ]);
    
            ...
        }
    
        public function putObject($bucket, $object_name, $source_file, $content_type = false, $acl = 'public-read', $filename = '')
        {
            try {
                $params = [
                    'Bucket'      => $bucket,
                    'Key'         => $object_name,
                    'SourceFile'  => $source_file,
                    'ACL'         => $acl,
                ];
    
                if ($content_type) $params['ContentType'] = $content_type;
                if ($filename) $params['ContentDisposition'] = 'attachment; filename="' . $filename . '"';
    
                $result = $this->handle->putObject($params);
    
                ...
            }
            catch(Exception $e)
            {
                ...
            }
        }
    

    【讨论】:

      【解决方案5】:

      看起来 :response_content_disposition 在 presigned_url 方法中没有记录。这对我有用

          signer = Aws::S3::Presigner.new
          signer.presigned_url(:get_object, bucket: @bucket, key: filename, 
          response_content_disposition: "attachment; filename =#{new_name}")
      

      【讨论】:

        【解决方案6】:

        对于 Java AWS SDK 下面的代码片段应该可以完成这项工作:

        GeneratePresignedUrlRequest generatePresignedUrlRequest = 
                        new GeneratePresignedUrlRequest(s3Bucket, objectKey)
                        .withMethod(HttpMethod.GET)
                        .withExpiration(getExpiration());
        
        ResponseHeaderOverrides responseHeaders = new ResponseHeaderOverrides();
        responseHeaders.setContentDisposition("attachment; filename =\"" + fileName + "\"");
        
        generatePresignedUrlRequest.setResponseHeaders(responseHeaders);
        

        【讨论】:

          【解决方案7】:

          通过 C# 使用 AWSSDK,

          GetPreSignedUrlRequest request = new GetPreSignedUrlRequest
          {
              BucketName = BucketName,
              Key = Key,
              Expires = DateTime.Now.AddMinutes(25) 
          };
          
          request.ResponseHeaderOverrides.ContentDisposition = $"attachment; filename={FileName}";
          
          var url = s3Client.GetPreSignedURL(request);
          

          【讨论】:

          • 感谢您,我能够将其翻译为在我的 VB.NET 项目中使用:request.ResponseHeaderOverrides.ContentDisposition = "attachment; filename=""my-new-filename-here.txt"""
          【解决方案8】:

          使用python和boto v2:

              conn = boto.connect_s3(
                  AWS_ACCESS_KEY_ID,
                  AWS_SECRET_ACCESS_KEY,
                  host=settings.AWS_S3_HOST,
              )
              b = conn.get_bucket(BUCKET_NAME)
              key = b.get_key(path)
              url = key.generate_url(
                  expires_in=60 * 60 * 10,  # expiry time is in seconds
                  response_headers={
                      "response-content-disposition": "attachment; filename=foo.bar"
                  },
              )
          

          【讨论】:

            猜你喜欢
            • 2020-03-05
            • 1970-01-01
            • 2020-05-29
            • 2014-11-28
            • 1970-01-01
            • 1970-01-01
            • 2017-02-21
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多