メモ用紙

 | 

2014年1月23日

Content-Disposition ヘッダを使って日本語のファイル名でダウンロードさせる

ブラウザに次のような HTTP レスポンスヘッダを出力すると、任意のファイル名で出力内容をダウンロードさせることができる。

Accept-Ranges: bytes
Content-Type: application/octet-stream
Content-Disposition: attachment; filename=ファイル名
Content-Length: ファイルサイズ

問題はファイル名が日本語の文字など、ASCII 以外の文字を含む場合である。ASCII 以外の文字をエンコードする方法はさまざまあり、ブラウザによって対応がまちまちである。

そこで次の PHP コードを用いてテストを行った。

手元の環境でテストした結果はこちら。

Opera 18Chrome 31Firefox 26Opera 12MSIE 8MSIE 11Safari 4-osxSafari 5.1-win
UTF-8 RawOKOKOKOKNGNGNGOK
UTF-8 URL EncodedOKOKNGOKOKOKNGOK
UTF-8 Base64OKOKOKNGNGNGNGNG
RFC 2231OKOKOKOKNGOKNGNG
Shift_JIS RawOKOKOKOKOKOKOKOK
Shift_JIS URL EncodedNGNGNGNGNGNGNGNG

比較的最近のブラウザでは 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);

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/scientre/20140123/http_attachment_filename
 |