PHPでダウンロードさせるファイル名がIEで文字化けする件


問題はどんな言語で書いても起こることですが、たまたま仕事でPHPつかってたときにぶつかったのでメモしておきます。

追記

2017/11/02: Edgeでも通用するようです。https://github.com/netcommons/NetCommons2/issues/126

ファイル名が化ける

PHPでファイルアップローダをつくっていました。

動作確認はUbuntu 14.04のFirefox30をつかっていましたが、社内ではIE11がデフォ。「一応やっとくか」とIE11で動かしたら、日本語のファイル名が見事に化けました。

またお前か、IE!チキショー

Slim Frameworkをつかっているので、こんなコードになっています。

$app->response->setStatus(200);
$app->response->headers->set('Content-Type', $type);
$app->response->headers->set('Content-Disposition', 'attachment; filename='.$filename);
$app->response->headers->set('Content-Length', $size);
$app->response->setBody(file_get_contents($filepath));

UTF-8からShift-JISに変換してやった

ならば、ということで、

$app->response->headers->set('Content-Disposition', 'attachment; filename='.mb_convert_encoding($filename, 'SJIS-win', 'UTF-8');

などとしてやったわけですが、今度はLinuxで化けてしまって、会社のみんなはいいけど自分たちはすごく不便、という状況に。

ちくしょうめ。

IEを使っているかどうか判断?

ググってみると、UserAgentの文字列をみてSJISかUTF-8か判断している記事が多く見つかったのですが、IE11になったときにUserAgentからMSIEの表記が消えてTridentになりましたよね。またそういうのがないとも限らないので、できるだけ避けたいものであります。

最終的に...

Content-Dispositionattachment; filename*=UTF-8''URLエンコードされたファイル名などとすれば良いことが判明。仕様はRFC6266だそうです。

RFC6266のこの書式をサポートしているのはIE9以上、(少なくとも)Firefox22以上、Safari6以上(Safari5はダメらしい)ということが、http://greenbytes.de/tech/tc2231/ からよめました。

最終的にはこんなコードになりました。一応動いています。

$app->response->setStatus(200);
$app->response->headers->set('Content-Type', $type);
$app->response->headers->set('Content-Disposition', 'attachment; filename*=UTF-8\'\''.rawurlencode($filename);
$app->response->headers->set('Content-Length', $size);
$app->response->setBody(file_get_contents($filepath));

結論

ファイル名はASCII文字以外を禁止にする運用が素敵。もっと言えば文字化けを(゚ε゚)キニシナイ!!

どうしても使わなきゃいけない時は、Content-Disposition: attachment; filename*=UTF-8''URLエンコードされたファイル名を送ってあげる。

参考

http://support.microsoft.com/kb/436616/ja
http://tools.ietf.org/html/rfc6266