ファイルアップローダーのような仕組みのシステムを作っています。
アップローダーと一緒に、ダウンロード用のURLを発行する機能も実装しているのですが、想定した挙動をしません。
具体的には目的ごとに2つのファイル名があり、ダウンロードされたファイルは後者のファイル名にしたいです。
- ストレージにアップロードするファイル名(重複しないランダム文字列)
- ダウンロード用のファイル名(日本語)
S3から署名URLを発行していた時は実装できていました。
CloudFront経由で署名URLで発行するように調整したところ、ダウンロードしたファイル名を変更できなくなりました。
CloudFrontでファイル名を指定する方法は無いでしょうか?
システムの概要
S3バケットにファイルをアップロードします。
ダウンロードしたいユーザーには、別途ダウンロードURLを通知します。
通知に記載されたダウンロードURLを経由して、S3からファイルをダウンロードします。
以前までの仕様と変更した経緯
元はS3の署名URL機能でダウンロードリンクを作成していました。
laravelで開発していますので、下記の様に実装できていました。
php
1$disk = Storage::disk(env('STORAGE_TYPE', 's3')); 2return $disk->temporaryUrl( 3 'upload/abcdefghi.pdf', 4 now()->addDay(1), 5 [ 6 'ResponseContentDisposition' => 'attachment; filename=' . rawurlencode('ファイル名.pdf') 7 ] 8);
しかし、生成されたリンクがNotFound(404)やForbidden(403)の場合、XMLがパースされたエラーページになってしまいます。
パースされた情報だと、ユーザーに状況が伝わらないため、CloudFrontのエラー出し訳をすることにしました。
状況
CloudFront経由してダウンロードURLの生成、URLからファイルの取得はできます。
エラーは発生していない様です。
ファイル名の指定と、(本件とは別ですが)ファイルのダウンロードが出来ません。(ブラウザで表示されてしまいます。)
後述しますが、response-content-disposition=attached; filename=(ファイル名)
を記述してもそもそも、効いていない(パラメータの解析?)がされていないように思います。
環境
アプリケーション
下記の環境で実装しています
環境名 | バージョン |
---|---|
php | 7.3.13 |
aws-sdk-php | 3.109 |
laravel | 5.8.* |
処理
S3については前述した通りです。
CloudFrontについては下記の様な処理を書いています。
php
1use Aws\CloudFront\CloudFrontClient; 2use Aws\Exception\AwsException; 3 4public function getCloudFrontDownloadUrl() { 5 $client = new CloudFrontClient([ 6 'profile' => 'default', 7 'region' => env('AWS_DEFAULT_REGION', 'ap-northeast-1'), 8 'version' => 'latest' 9 ]); 10 11 // 有効期限 12 $expires = time() + 24 * 60 * 60; 13 14 // URL設定 15 $cloudFrontDomain = 'https://xxxxxxx.cloudfront.net/'; 16 $url = $cloudFrontDomain 17 . 'upload/abcdefghi.pdf' 18 . "?response-content-disposition=attachment; filename=" . rawurlencode('ファイル名.pdf'); 19 20 return $client->getSignedUrl([ 21 'url' => $url, 22 'expires' => $expires, 23 'private_key' => '(プライベートキーファイル.pem)', 24 'key_pair_id' => '(アクセスキーID)' 25 ]); 26}
ファイル名の指定は下記の処理を、URLのパラメータとして追加する事で対応できると考えていました。
response-content-disposition=attachment; filename=(ファイル名)
公式ドキュメントでは確認できなかったのですが、CloudFrontの署名URL実装方法を見ていると下記の様な指定方法をしていたのでこの方法を取りました。
サーバー
AWSで構築しています。
CloudFront、S3で特筆すべき設定は下記です。
CloudFrontの設定
CloudFrontとS3を連携しますので、デフォルトの状態から下記の設定を変更しています。
それ以外は、デフォルトの設定のままです。
設定名 | 設定値 |
---|---|
Origin Domain Name | test_bucket.s3.amazonaws.com |
Restrict Bucket Access | yes |
Grant Read Permissions on Bucket | Yes, Update Bucket Policy |
S3の設定
前項でポリシーをアップデートしていますので下記の様な構成になっています。
{ "Version": "2008-10-17", "Id": "PolicyForCloudFrontPrivateContent", "Statement": [ { "Sid": "1", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ***" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::test_bucket/*" } ] }

バッドをするには、ログインかつ
こちらの条件を満たす必要があります。