質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.63%

【CloudFront】【PHP】署名URL発行時に独自のファイル名を指定したい

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 97

hasshy

score 68

ファイルアップローダーのような仕組みのシステムを作っています。
アップローダーと一緒に、ダウンロード用のURLを発行する機能も実装しているのですが、想定した挙動をしません。

具体的には目的ごとに2つのファイル名があり、ダウンロードされたファイルは後者のファイル名にしたいです。

  • ストレージにアップロードするファイル名(重複しないランダム文字列)
  • ダウンロード用のファイル名(日本語)

S3から署名URLを発行していた時は実装できていました。
CloudFront経由で署名URLで発行するように調整したところ、ダウンロードしたファイル名を変更できなくなりました。

CloudFrontでファイル名を指定する方法は無いでしょうか?

システムの概要

S3バケットにファイルをアップロードします。
ダウンロードしたいユーザーには、別途ダウンロードURLを通知します。
通知に記載されたダウンロードURLを経由して、S3からファイルをダウンロードします。

イメージ説明

以前までの仕様と変更した経緯

元はS3の署名URL機能でダウンロードリンクを作成していました。
laravelで開発していますので、下記の様に実装できていました。

$disk = Storage::disk(env('STORAGE_TYPE', 's3')); 
return $disk->temporaryUrl(
    'upload/abcdefghi.pdf',
    now()->addDay(1),
    [
        'ResponseContentDisposition' => 'attachment; filename=' . rawurlencode('ファイル名.pdf')
    ]
);

しかし、生成されたリンクが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については下記の様な処理を書いています。

use Aws\CloudFront\CloudFrontClient;
use Aws\Exception\AwsException;

public function getCloudFrontDownloadUrl() {
    $client = new CloudFrontClient([
        'profile' => 'default',
        'region' => env('AWS_DEFAULT_REGION', 'ap-northeast-1'),
        'version' => 'latest'
    ]);

    // 有効期限
    $expires = time() + 24 * 60 * 60;

    // URL設定
    $cloudFrontDomain = 'https://xxxxxxx.cloudfront.net/';
    $url = $cloudFrontDomain
        . 'upload/abcdefghi.pdf'
        . "?response-content-disposition=attachment; filename=" .  rawurlencode('ファイル名.pdf');

    return $client->getSignedUrl([
        'url' => $url,
        'expires' => $expires,
        'private_key' => '(プライベートキーファイル.pem)',
        'key_pair_id' => '(アクセスキーID)'
    ]);
}

ファイル名の指定は下記の処理を、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/*"
        }
    ]
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

+1

CloudFront(Behaviors)の設定

  • 署名URLの利用の設定をします。
  • Query Stringでresponse-content-dispositionの利用の許可をします。

Distributionsから対象のDistributionを選び
BehaviorsタブからEdit

設定名 設定値
Query String Forwarding and Caching Forward all, cache based on whitelist
Query String Whitelist response-content-disposition
Restrict Viewer Access Yes
Trusted Signers Self

処理の変更

  • URLを作成する部分はファイル名をURLエンコードした上で、全体をもう一度URLエンコードします。
$filename = rawurlencode('ファイル名.pdf');
$disposition = rawurlencode('attachment; filename=' . $filename);
$url = $cloudFrontDomain. 'upload/abcdefghi.pdf?response-content-disposition=' . $disposition;

上記でいかがでしょうか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.63%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる