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-клиента.

Другие вопросы по тегам