tenpuのファイル名文字化け対策について

f:id:gurimmer:20171030103436j:plain フリー写真素材ぱくたそ  | ©:すしぱく

こんにちは、主にtenpuの開発を行っている @gorou です。 tenpuで複数ファイルアップロードを行った場合、ダウンロードはzipで圧縮して提供しています。この時によく発生するのが文字化けです。 tenpuで実施した文字化け対策について紹介します。

文字化けが起こる原因

2種類の問題が同時に発生します。

1.ファイル名の文字コード

WindowsはCP932(内部的にはUTF-16)*1、Mac OS XやLinuxなどはUTF-8が利用されています。そのため、Windows→Mac、Mac→Windowsでファイルを受け渡しすると、文字コードが異なるため文字化けしてしまいます。

2.zip展開時の文字エンコード

zipを展開する際、OSによって強制的に固定の文字エンコードが行われてしまい、文字化けするケースがあります。

1. Windows7 より前

文字コードがCP932のため、zip展開時は強制的にCP932のエンコードが行われます。 CP932以外の文字コードの場合、文字化けする可能性があります。よくMacのzipをWindowsで開くと文字化けする原因は、この強制的にCP932でエンコードを行ってしまうWindows OS側の問題でした(受け手側からするとMacが悪いと思われていますが...)

2. Windows7

内部の文字コードはUnicodeに変わりましたが、表示はCP932という状態になりました。ただし、zip展開時の強制CP932エンコードは行われてしまうため、文字化けする可能性があります。ただ、任意適用のパッチがMicrosoftから提供されており、パッチを適用すると文字コードを判別してエンコードを行ってくれるようになるため、文字化けせずzip展開が行えるようになります。*2

https://support.microsoft.com/ja-jp/help/2704299/japanese-characters-in-file-names-are-displayed-as-garbled-text-after

3. Windows7 より後

Windows7同様、内部の文字コードはUnicodeに変わりましたが、表示はCP932という状態です。zip展開時に文字コードを判別してエンコードを行ってくれます。そのため、文字コードの変換は必要なくなりました。

4. Mac OS X

zip展開時にUTF-8エンコードが行われるため、基本的には問題ありません。UTF-8-macとUTF-8で正規化形式が異なるという問題はありますが、今回は省略します。

5. Linux

Linuxが利用しているunzipコマンドは日本語に対応していないため、Linux上でunzipする際、文字化けるので注意が必要です。

実施した対策

カンペキな対応は難しいのですが、UserAgentからOSを判定し、OSにあったファイル名の文字エンコードを実施したzipファイルを提供することで対策しました。

事前処理

ファイル名に対して、UTF-8CP932に変換できない文字の置換と、ファイル名に利用できない文字・記号などの除外を行います。詳細は省きます。

zip圧縮

3種類のzipを作成しました。Mac向けzip、Windows7向けzip、その他Windows向けzip

Mac向けzip

MacおよびWindowsどちらのファイル名でも対応できるよう、以下の優先順位でファイル名をエンコードします。

<?php

$filename = mb_convert_encoding($filename, 'UTF-8', 'ASCII,JIS,UTF-8,eucJP-win,SJIS-win');

Windows7向けzip

CP932で強制的にエンコードされるため、CP932に変換しておきます。

※提供されているパッチは、任意適用のパッチのため適用者はとても少ないと判断しました。以下のコードは、パッチ適用者には文字化けしてしまいます。

<?php

$filename = mb_convert_encoding($filename, 'SJIS-win', 'ASCII,JIS,UTF-8,eucJP-win,SJIS-win');

その他Windows向けzip

事前処理で変換できない文字の置換をおこなっているので、OS側の文字コード自動判別で問題なく変換ができます。そのためファイル名のエンコード変換は行いません。(tenpuでは、Windows7以前のWindows OSは対象外としています)

ダウンロード

ダウンロードページに訪れた人のUserAgentを見て、OSを判別して上の3種類のzipの中から最適なzipを提供することで文字化けを回避しています。

まとめ

ファイル名およびzip展開時の文字化け対策についてまとめました。

文字化けの再現と対策を色々なパターンで試していた時、Aを実施するとXOKだけどYNGBを実施するとXNGYOKみたいな状況が発生してとても混乱しました。まだまだ一部の文字で文字化けが発生することもあると思うので、見つけられた方ご連絡をお待ちしております。 文字化けで困っている方に参考になればと思います。

*1:PHPでのコード名はSJIS-winが使われています

*2:パッチの適用は自己責任にて適用ください