S3のContent-Dispositionのブラウザ対応について調査してみた

こんにちは、@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 5987filename*=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 ヘッダーを指定したい場合、 getCommandResponseContentDisposition を渡すと指定できます。

検証結果

  • 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エンジニアの方はこちら

参考サイト