Amazon S3 Изменить имя загрузки файла
У меня есть файлы, хранящиеся на S3 с GUID в качестве имени ключа.
Я использую предварительно подписанный URL для загрузки в соответствии с S3 REST API
Я сохраняю оригинальное имя файла в моей собственной базе данных. Когда пользователь щелкает, чтобы загрузить файл из моего веб-приложения, я хочу вернуть его оригинальное имя файла, но в настоящее время все, что он получает, - это GUID. Как мне этого добиться?
Мое веб-приложение работает в Salesforce, поэтому я не могу контролировать ответ. Все файлы перенаправляются на веб-сервер, а затем переименовываются из-за ограничений регулятора.
Могу ли я использовать HTML-редирект, мета-обновление, Javascript? Есть ли какой-нибудь способ изменить имя загружаемого файла для S3 (единственное, что я могу придумать, это скопировать объект на новое имя, загрузить его, а затем удалить его).
Я хочу избежать создания группы для каждого пользователя, так как у нас будет много пользователей, и все же я не гарантирую, что каждый файл в каждой группе будет иметь уникальное имя
Любые другие решения?
11 ответов
Я полагаю, что ваш кросс отправил эти вопросы на форум Amazon S3, но ради других я хотел бы опубликовать ответ здесь:
Если для каждого объекта S3 существует только одно "имя пользователя", то вы можете установить заголовок Content-Disposition в вашем файле s3, чтобы установить имя загружаемого файла: Content-Disposition: attachment; имя файла =foo.bar
Справедливости ради, я хотел бы отметить, что это был не я, чтобы дать правильный ответ на форуме Amazon, и все кредиты должны идти Колину Роудсу;-)
Хотя принятый ответ правильный, я нахожу его очень абстрактным и трудным для использования.
Вот фрагмент кода 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...]
}
Пожалуйста, примите к сведению ResponseContentDisposition
атрибут params
объект передан в s3.getSignedUrl
функция.
Дополнительную информацию см. В разделе "Функция-документ getObject" по адресу http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html.
В начале января 2011 года S3 добавил переопределение заголовка запроса. Эта функциональность позволяет вам "динамически" изменять заголовок Content-Disposition для отдельных запросов.
Смотрите документацию S3 по получению объектов для более подробной информации.
С 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);
Для 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);
Столкнулся с похожей проблемой. Необходимо создать предварительно подписанный URL-адрес для загрузки 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
Используя 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"
},
)
Это выглядит так: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}")
Я потратил несколько часов, чтобы найти это решение.
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}`
)
});
У меня такая же проблема, я решил ее, установив http-заголовок "content-disposition" при отправке файла на S3, версия SDK - это AWS SDK для PHP 3.x. вот документ http://docs.amazonaws.cn/en_us/aws-sdk-php/latest/api-s3-2006-03-01.html
часть моего кода
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)
{
...
}
}
При работе с AWS SDK для Java 2.x вы можете следовать этому руководству по работе с заранее назначенными URL-адресами Amazon S3 . Независимо от модели запроса, которую вы создаете, вы можете установитьContent-Disposition
заголовок с помощью этих методов
Примеры
PutObjectRequest
...
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(bucketName)
.key(keyName)
.contentType(contentType)
.metadata(metadata)
.contentDisposition("attachment;filename=" + displayName)
.build();
PutObjectPresignRequest putObjectPresignRequest = PutObjectPresignRequest.builder()
.signatureDuration(Duration.ofMinutes(10))
.putObjectRequest(putObjectRequest)
.build();
PresignedPutObjectRequest presignedPutObjectRequest = presigner.presignPutObject(putObjectPresignRequest);
...
GetObjectRequest
...
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
.bucket(bucketName)
.key(keyName)
.responseContentDisposition("attachment;filename=" + displayName)
.build();
GetObjectPresignRequest getObjectPresignRequest = GetObjectPresignRequest.builder()
.signatureDuration(Duration.ofMinutes(60))
.getObjectRequest(getObjectRequest)
.build();
PresignedGetObjectRequest presignedGetObjectRequest = presigner.presignGetObject(getObjectPresignRequest);
...
Не проверено. Я думаю, что это правильный путь для Java-клиента.