Content-Disposition ヘッダを使って日本語のファイル名でダウンロードさせる
ブラウザに次のような HTTP レスポンスヘッダを出力すると、任意のファイル名で出力内容をダウンロードさせることができる。
Accept-Ranges: bytes Content-Type: application/octet-stream Content-Disposition: attachment; filename=ファイル名 Content-Length: ファイルサイズ
問題はファイル名が日本語の文字など、ASCII 以外の文字を含む場合である。ASCII 以外の文字をエンコードする方法はさまざまあり、ブラウザによって対応がまちまちである。
そこで次の PHP コードを用いてテストを行った。
手元の環境でテストした結果はこちら。
Opera 18 | Chrome 31 | Firefox 26 | Opera 12 | MSIE 8 | MSIE 11 | Safari 4-osx | Safari 5.1-win | |
---|---|---|---|---|---|---|---|---|
UTF-8 Raw | OK | OK | OK | OK | NG | NG | NG | OK |
UTF-8 URL Encoded | OK | OK | NG | OK | OK | OK | NG | OK |
UTF-8 Base64 | OK | OK | OK | NG | NG | NG | NG | NG |
RFC 2231 | OK | OK | OK | OK | NG | OK | NG | NG |
Shift_JIS Raw | OK | OK | OK | OK | OK | OK | OK | OK |
Shift_JIS URL Encoded | NG | NG | NG | NG | NG | NG | NG | NG |
比較的最近のブラウザでは RFC 2231 形式に対応しているので、通常はこの形式を使えば問題は少ないだろう。
また古いブラウザでは、Shift_JIS で表現できる文字列の場合、Shift_JIS 文字列をそのまま記述すれば正しい結果が得られる。
PHP でのコード例
以上を踏まえてコードを記述すると、次のようになる。
<?php header('Accept-Ranges: bytes'); header('Content-Type: application/octet-stream'); if (preg_match('/\bMSIE\b|\bSafari [12345]\b/', getenv('HTTP_USER_AGENT'))) { header('Content-Disposition: attachment; filename="' . mb_convert_encoding($filename, 'Shift_JIS', 'UTF-8') . '"'); } else { header('Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode($filename)); } header('Content-Length: ' . filesize($file)); readfile($file);
注意点
ユーザの環境においてファイル名として使えない文字を指定したとき、その動作はブラウザによって異なる。次のページに書かれているようにエスケープするとよい。
<?php $filename = preg_replace('/\\xE2\\x80\\xAE|\\xE2\\x80\\xAD/', '', $filename); $filename = preg_replace('/\\s/u', '_', $filename); $filename = preg_replace('{[\\\\/:*?"<>|]}', '', $filename);