更改文件下载名称

我有文件存储在 S3与 GUID 作为关键名称。

我使用一个预先签署的网址下载按 S3 REST API

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

我的网络应用程序是在销售力量,所以我没有太多的控制做 response。重定向所有下载到网络服务器的文件,然后重命名,由于总督的限制。

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

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

还有别的办法吗?

62973 次浏览

我猜你的交叉张贴这个问题到 亚马逊 S3论坛,但为了其他人的缘故,我想张贴在这里的答案:

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

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

为了公平起见,我想提一下,在亚马逊论坛上提供正确答案的不是我,所有的荣誉都应该归于科林 · 罗兹(Colin Rhodes) ; -)

在2011年1月初,S3添加了请求头重写。此功能允许您“动态”更改单个请求的 Content-Disposition 头。

有关详细信息,请参阅 S3获取对象的文档

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

下面是一段 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属性。

更多信息请参见 getObject 函数 doc at http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#getObject-property

我有同样的问题,我解决了它设置 http 头“内容-处置”,同时提交文件到 S3,SDK 版本是 AWS SDK 的 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)
{
...
}
}

看起来: response _ content _ position 在预定的 _ url 方法中没有文档记录

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

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

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


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


generatePresignedUrlRequest.setResponseHeaders(responseHeaders);

用 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);

使用 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"
},
)

我花了几个小时才找到解决办法。

const { CloudFront } = require("aws-sdk");
const url = require("url");


const generateSingedCloudfrontUrl = (path) => {
const cloudfrontAccessKeyId = process.env.CF_ACCESS_KEY;
const cloudFrontPrivateKey = process.env.CF_PRIVATE_KEY;
const formattedKey = `${"-----BEGIN RSA PRIVATE KEY-----"}\n${cloudFrontPrivateKey}\n${"-----END RSA PRIVATE KEY-----"}`;
const signer = new CloudFront.Signer(cloudfrontAccessKeyId, formattedKey);
//  12 hours
const EXPIRY_TIME = 43200000;


const domain = process.env.CF_DOMAIN;
const signedUrl = signer.getSignedUrl({
url: url.format(`https://${domain}/${path}`),
expires: Math.floor((Date.now() + EXPIRY_TIME) / 1000),
});
return signedUrl;
};


const fileName = "myFile.png";
const result = generateSingedCloudfrontUrl(
`originals/orgs/originals/MSP/1539087e-02b7-414f-abc8-3542ee0c8420/1644588362499/Screenshot from 2022-02-09 16-29-04..png?response-content-disposition=${encodeURIComponent(
`attachment; filename=${fileName}`
)
});

面临着类似的问题。需要生成预签名的网址下载 pdf 从 s3但与不同的名称其他什么在 s3桶。通过使用:-解决了这个问题

使用 Python boto3

file_download_url = client.generate_presigned_url(
ClientMethod = "get_object",
ExpiresIn = 3600,
Params = {
"Bucket": "s3_bucket_name",
"Key": "s3_key",
"ResponseContentDisposition": "attachment; filename=file_name.pdf",
"ResponseContentType" : "application/pdf"
}

)

参考资料:-https://github.com/boto/boto3/issues/356