こんにちは、@gorou_178です。 主にtenpu の開発を担当しています。
tenpuはAWSを利用して構築しており、特にS3をよく使うのでS3について書こうと思います。
ユーザにサービス側で指定したファイル名でファイルをダウンロードさせたい場合、 Content-Disposition
ヘッダーでファイル名を指定します*1
しかし、Content-Dispositionのattachment指定はブラウザによって対応状況がバラバラで対応に苦しんだ方も多いかと思います。S3に対してファイル名指定ダウンロードさせるには、S3にContent-Dispositionを指定するのですが、エンコード方法によってはエラーになったりしたため、S3側の対応状況を調査してみました。
結論
最新のブラウザでは、 S3に対して Content-Dispositionのfilenameを RFC 5987
で指定すると問題なくファイル名を指定できる
ということが分かりました。その詳細をまとめてみました。
attachmentのfilename指定方法を決める
Content-Dispositionのattachmentのfilenameの書き方として以下の6つに注目して検証してみました。
Content-Dispositionヘッダのattachment値を作成
RFC 5987について
まず前提としてRFC 2231は引数(MIME)についての仕様があります。filename=hoge
の書き方の仕様が書かれてます。その拡張仕様となるのがRFC 5987。 filename*=hoge
と書くことで長い名前や非ASCII文字も扱えるようになるという仕様が書かてれいます。
以下サイトでContent-Dispositionについての歴史やRFC翻訳を行ってくれているのでわかりやすいと思います。
https://wiki.suikawiki.org/n/Content-Disposition
https://wiki.suikawiki.org/n/filename
テストコード
各ブラウザでファイルをダウンロードした場合の挙動を以下のプログラムで検証しました。
※1 検証するためにはComposerでAWS SDK PHP v3が必要です
※2 環境変数 AWS_ACCESS_KEY_ID
AWS_SECRET_KEY
には AmazonS3ReadOnlyAccess
権限をもった IAMユーザのアクセスキーを指定してください
S3のContent-Dispositionダウンロードテスト
AWS SDK PHP v2を利用している方で、 getObjectUrl
で署名付きURLを取得している方はv3系で変更が入り取得できなくなっているため注意が必要です。
v3系でS3の署名付きURLを取得したい場合は、 createPresignedRequest
を利用します。
AWS SDK PHP v3を利用した署名付きURLの取得方法
S3に Content-Disposition
ヘッダーを指定したい場合、 getCommand
に ResponseContentDisposition
を渡すと指定できます。
検証結果
- S3の元のファイル名は
s3-test-file.png
- ダウンロードファイル名は
日本語.png
を指定して検証
エンコード方法 | Chrome 52 | Firefox 48.0.2 | Safari 9.1.3 |
---|---|---|---|
UTF8 Raw | ×(InvalidArgumentエラー) | ×(InvalidArgumentエラー) | ×(InvalidArgumentエラー) |
UTF-8 URL Encoded | ◯ | ×(ファイル名が正しく指定できない) | ×(ファイル名が正しく指定できない) |
UTF-8 Base64 Encoded | ◯ | ◯ | ×(ファイル名が正しく指定できない) |
RFC 5987 | ◯ | ◯ | ◯ |
Shift_JIS Raw | ×(InvalidArgumentエラー) | ×(InvalidArgumentエラー) | ×(InvalidArgumentエラー) |
Shift_JIS URL Encoded | ×(ファイル名が正しく指定できない) | ×(ファイル名が正しく指定できない) | ×(ファイル名が正しく指定できない) |
エンコード方法 | Edge | IE11 |
---|---|---|
UTF8 Raw | ×(400 エラー) | ×(400 エラー) |
UTF-8 URL Encoded | ◯ | ◯ |
UTF-8 Base64 Encoded | ×(ファイル名が正しく指定できない) | ×(ファイル名が正しく指定できない) |
RFC 5987 | ◯ | ◯ |
Shift_JIS Raw | ×(400 エラー) | ×(400 エラー) |
Shift_JIS URL Encoded | ×(ファイル名が正しく指定できない) | ×(ファイル名が正しく指定できない) |
RFC 5987
が各最新ブラウザで問題なくファイル名指定ダウンロードができることが分かりました。エンコード方法によっては400エラーになってしまったり、ファイル名が正しく指定できなかったりとサポートしていないことが分かります。
注意点
S3のメタデータとして
Content-Type
に画像系(image/png
)などが指定されているとダウンロードではなく画像をブラウザが開いてしまうため、ダウンロードさせたい場合は、Content-Type: binary/octet-stream
に変更しておくWin → Macなど異なるOSでファイルを受け渡した場合、文字コードの問題でファイル名の文字化けが発生します。UTF-8→CP932の変換がNGである文字の置換であったり、制御文字の削除や機種依存文字の置換といった処理が必要になります
まとめ
S3のContent-Dispositionの対応状況を調査しました。このあたり情報が古かったり、そもそも対応状況をまとめたものがなかったので簡単ではありますがまとめてみました。 古いブラウザだとこの通りにいかない可能性があるため、利用する場合は注意ください。
サービス開発にはこのような地味な調査が時には必要ですが、進むべき場所を探して見つかった時はとても嬉しくなります。 そんなサービス開発に携わってみたいというエンジニアを募集してます。ご興味のある方はぜひ。
PHPカンファレンス2016に参加したPHPエンジニアの方はこちら
参考サイト
*1:PATH_INFOを利用する方法もあります