Amazon S3更改文件下载名称


101

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

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

我将原始文件名存储在自己的数据库中。当用户单击以从我的Web应用程序下载文件时,我想返回其原始文件名,但是目前他们所获得的只是一个GUID。我该如何实现?

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

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

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

还有其他解决方案吗?


6
另一个合法的问题被关闭了!该问题和答案也比绝大多数常见问题/答案拥有更多的投票...
DeepSpace101

是的,面对这样一个事实,即在将来甚至现在都不能保证一定意义的情况下,我们尝试任意地组织事情显然更为重要。当人们开心地询问和回答与软件行业有关的问题时会发生什么?谁真正因为这个组织找到答案?谁能发现整个行业中一致且清晰定义的软件组织结构?如果没有这个清晰的定义,它将持续大量的时间进行组织,那将是相当愚蠢的时间浪费……
Derek Litz

1
我找到了这个答案,我对此组织没有任何疑问。
以赛亚·特纳2014年

Answers:


83

我想您的十字架在Amazon S3论坛上发布了这个问题,但是为了其他人,我想在这里发布答案:

如果每个S3对象只有一个“用户文件名”,则可以在s3文件上设置Content-Disposition标头以设置下载文件名:Content-Disposition:附件;filename = foo.bar

为了公平起见,我想提一下,在亚马逊论坛上提供正确答案不是我本人,所有荣誉应归科林·罗德斯所有;-)


是的,谢谢你提醒我。另一条重要信息是添加Content-Disposition时,它区分大小写,并且不需要x-amz前缀。
Daveo'4

3
请看下面的答案,自2011年1月起,也可以根据每个GET请求进行操作。因此,可以根据需要拥有多个“用户文件名”。
Fabi)2015年

2
我需要在文件名中添加引号以使其起作用,所以我的标头是:Content-Disposition:附件;filename =“ foo.bar”
nathancahill

1
在c#中:request.ResponseHeaderOverrides.ContentDisposition =“ attachment; filename = foo.bar”;
阿米尔M

2
您可以即时覆盖响应标头:https://...example.txt?response-content-disposition=attachment;filename=foo.bar
alaster

46

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

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

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...]

}

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

http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#getObject-property的getObject函数文档下的更多信息


也许这并不明显,我会尽力解释。这个想法是我将上传文件的名称列表保存在数据库中,然后当有人要下载它时,我使用originalFilenameas参数执行lambda 。
Pawel


3

对于下面的代码片段,对于Java AWS开发工具包,可以执行以下操作:

GeneratePresignedUrlRequest generatePresignedUrlRequest = 
                new GeneratePresignedUrlRequest(s3Bucket, objectKey)
                .withMethod(HttpMethod.GET)
                .withExpiration(getExpiration());

ResponseHeaderOverrides responseHeaders = new ResponseHeaderOverrides();
responseHeaders.setContentDisposition("attachment; filename =\"" + fileName + "\"");

generatePresignedUrlRequest.setResponseHeaders(responseHeaders);

3

通过使用AWSSDK的C#,

GetPreSignedUrlRequest request = new GetPreSignedUrlRequest
{
    BucketName = BucketName,
    Key = Key,
    Expires = DateTime.Now.AddMinutes(25) 
};

request.ResponseHeaderOverrides.ContentDisposition = $"attachment; filename={FileName}";

var url = s3Client.GetPreSignedURL(request);

0

我遇到了同样的问题,我通过在将文件提交到S3时设置http标头“ content-disposition”来解决它,该SDK版本是适用于PHP 3.x的AWS开发工具包。这是文档http://docs.amazonaws.cn/zh_CN/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)
        {
            ...
        }
    }

0

看起来: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}")
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.