Opera で日本語名ファイルをアップロードすると送信される filename が文字化けする場合がある


Opera で日本語名ファイルをアップロードすると送信される filename が文字化けする場合がある。
しかしなぜか同じように送っても文字化けしないファイルもある。

原因も再現条件も分からないが、とりあえず現象レポート。

【2011-6-15   追記】

文字化け時に何が起こっているかは何となく分かったので、 末尾に追記あり。

日本語ファイルアップロードができるCGIプログラムを探しているという方はこちらをどうぞ
(日本語ファイルアップローダー「すぐ使えるCGI」)

基本のおさらい

フォーム送信

ファイルを送信する時は、フォームの enctype を「multipart/form-data」に指定する。

<form name=”form1″ method=”post” action=”show_postdata.cgi” ENCTYPE=”multipart/form-data”>file:<input type=”file” name=”uploadFile”/><br />
テキストフィールド:<input type=”text” name=”textfield”/><br />

<input type=”submit” value=”OK”/>
</form>

送信される内容

送信される形式は以下のようになる。(受け取り側のCGIで標準入力をそのまま書き出したもの。)

—————————–36022435010291
Content-Disposition: form-data; name=”uploadFile”; filename=”アップロードファイル.txt”
Content-Type: text/plainこの行からファイルの中身です。
あああ
ファイルはバイナリの場合もあります。
この行までファイルの中身です。
—————————–36022435010291
Content-Disposition: form-data; name=”textfield”

テキスト値
—————————–36022435010291–

ファイル名

送信された「filename=”アップロードファイル.txt”」の値をサーバ上のCGIプログラムなどでファイル名として解釈するのだが、この送信内容を作成しているのは送信元のブラウザの方なので、微妙に形式が異なる。

上記は Firefox の例だが、たとえば IE だとこの部分は

filename=”C:\Documents and Settings\user\デスクトップ\アップロードファイル.txt”

とローカルパスを付けた状態で送信してくる。(セキュリティ上どうかと思うけど、とりあえず不問。)

日本語ファイル名

最近のブラウザの場合大抵、ファイル名が日本語文字の場合、そのファイル名はフォームのページの文字コードで送信してくる。テキスト入力フィールドがある場合、テキストフィールドと同じ文字コードとなると思えばよい。
例えばフォームのページが Shift_JIS なら、ファイル名も Shift_JIS でのコード列になるし、ページが UTF-8 なら同じファイル名を UTF-8 コード列で送ってくる。
ただしこれもブラウザ依存なので、旧いブラウザだと必ずしもそうでない。

そして、日本語には「Shift_JIS」という、Perl プログラマにとっては厄介な文字コードがある。
Shift_JIS の場合、「表」や「申」や「ソ」の一部にIEが送ってくるローカルパス区切りの「\」であり、かつ Perl にとっては特殊文字である「\」を含んでいるのだ。

「申請書.doc」とか「分析結果表.xls」などのファイル名をブラウザはどう送ってくるか?

概要としては、以下の通り。

Firefox

ファイル名だけを、そのまま送ってくる。
例)filename=”申請書.doc”

IE

ファイルパスを含めて、そのまま送ってくる。
例)”C:\Documents and Settings\user\デスクトップ\申請書.doc”

この場合「申」の中にパス区切り「\」が含まれているので、プログラムで単純にファイル名を取り出してしまうと「請書.doc」というファイル名になるので注意が必要になる。

Opera

問題の Opera さんはファイル名だけを、特殊文字をエスケープして送ってくる。
例)filename=”申\請書.doc”

今回起きた問題

ということで、Opera はファイル名を自分なりに解釈してから送ってくれるらしいのだが、ファイル名によっては変な風にエンコードして送ってくる事が分かった。
どのエンコーディングスキームにも該当しないような不可解なコード列を送ってくる。
特定の文字列がある時という訳ではなく、何かの相互作用なようなのだが、不明。

参考データ表.xls –>OK
参考データ表.txt –>OK
参考.txt –>NG
参考表.txt –>NG
参考申請書.docx –> NG

例えば、「参考データ表.xlsx」をアップロードしてファイル名を unpack(“H*” , $filename) で見ると

8e51 8d6c 8366 815b 835e 955c5c 2e 78 6c 73 78

と期待どおりの結果になる。(Shift_JISフォーム。結果は分かち書き。以下同様。)

ところが「参考.txt」をアップロードして同様に処理すると

c3a5c3a8 2e 74 78 74

となる。

「参考申請書.docx」 だと

e89cbf e3828a efbfbd efbfbd e7ad8f e99ab2 e58cba e5b68c 2e 64 6f 63 78

となる。
途中で2回続けて現れる「efbfbd」は「Unicode には無い文字です」の「U+FFFD (REPLACEMENT CHARACTER) 」の UTF-8 での表現だけど…うーん?

これらの問題は、ファイルを2つ一緒に送信しても、「参考データ表.xls」の方はOK、「参考申請書.docx」 の方はNGと、文字化けするということ自体は再現性がある。
おまけに、EUC-JP フォームでも、Shift_JISフォームの場合と同じファイル名が文字化けした。OS(WinXP) の文字コードからの変換との関係かも知れない。
Mac (OS 10.2 )の場合、やはりOpera と NC ( Netscape Communicator )が別のパターンで文字化けした。

2011-6-15 追記:
Windows XP Opera の文字化け時に起こっている事

「参考申請書.txt」を例に挙げる。

現象のおさらい

ファイル 「参考申請書.txt」 をアップロードすると、「参考申請書」部分のコード列は
e89cbf e3828a efbfbd efbfbd e7ad8f e99ab2 e58cba e5b68c
となり、文字化け状態となる。

コード変換状況

「参考申請書」の  Shift_JIS でのコード列は 8e51 8d6c 905c 90bf 8f91 。
UTF-8 だと e58f82 e88083 e794b3 e8ab8b e69bb8 となる。

さて一方、文字化け後のコード列の先頭「e89cbf」は文字としては「蜿」、Shift_JIS エンコーディングスキームでは「e58f」になる。

つまりどういうわけかOperaはShift_JISで渡された(筈。Windows XP のファイルシステム内部コードは Unicode だが、アプリケーションに渡す時は Shift_JIS に変換している。)ファイル名を一旦 UTF-8 に変換した後、さらにそれを Shift_JIS として解釈して UTF-8 に変換している。コード列を期待しない位置で区切るので、該当なし文字なども出る。

「e58f」→  「蜿」→「e89cbf」
「82e8」→  「り」→「e3828a」
「80」→  (該当なし)→「efbfbd」
「83e7」→  (該当なし)→「efbfbd」
「94b3」→  「筏」→「e7ad8f」

ファイルによっては問題なく解釈できるので、おそらくコード判別に失敗したファイルについて色々工夫してくれるのだと思うが。
「該当なし」として efbfbd になってしまった文字を復元する事ができないので、サーバ側では解釈のしようがないですね…。
Opera さん 頑張って~。

他のパターンかもしれないパターン

「参.txt」の「参」の部分は、

Shift_JIS 表現「8E51」
UTF-8 だと「E58F82」

文字化けした後は「c3a5」。
これは、Unicode で 小文字の「a」の上にマルの付いた文字を表す U+00E5 の UTF-8 コード列。

「E5」 は Latin1 で同じ文字を表すが、ムリムリ Shift_JIS で解釈したとすると、残りの「8F82」をShift_JISの「盾」と解釈してもおかしくないので、ここは元の文字列を以下のように解釈したと考える方がいいのかもしれない。

「8」→(Latin1,ASCII で該当なし)→ 捨てちゃえ!
「E5」→(aにマル)→「c3a5」
「1」→(Latin1,ASCII で該当なし)→ 捨て

いずれにせよバグっぽいです。