ホーム   »  スポンサー広告  »     »  Java  »  ZIPファイル内の非UTF-8ファイル名

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

ZIPファイル内の非UTF-8ファイル名

Java では java.util.zip パッケージのクラスを使用して簡単に ZIP ファイルを扱うことが出来る。例えば以下のようなコードで ZIP ファイルの中に格納されているファイルを読み込むことが出来る (ファイルクローズは省略)。

import java.util.zip.*;


ZipFile file = new ZipFile("zipfile.zip");
Enumeration en = file.entries();
while(en.hasMoreElements()){
    ZipEntry entry = (ZipEntry)en.nextElement();
    String path = entry.getName();
    if(entry.isDirectory()){
        // ディレクトリの場合の処理
    } else {
        InputStream in = file.getInputStream(entry);
        // ファイルの場合の処理
    }
}

この標準機能にちょっとした問題がある。ほとんどの圧縮ツールがローカル環境のエンコーディング (Windows/Macintosh なら Shift_JIS、大抵のUnix は EUC-JP) でファイル名/ディレクトリ名を格納する一方で、Java 標準のこの機能は UTF-8 固定でしかファイル名を扱えないのだ。

例えば日本語ファイル名を含む ZIP ファイルに上記コードの entry.getName() は文字化けしたパスを取り出してしまう。さらに、この文字化けした ZipEntry から入力ストリームを取り出す file.getInputStream(entry) では null が返されるのである。

ZipFile を使わずに ZipInputStream から getNextEntry() を使った場合も、内部で同様のことが起きているのであろう、こちらは IllegalArgumentException が発生してしまうのだ。

java.lang.IllegalArgumentException
    at java.util.zip.ZipInputStream.getUTF8String(ZipInputStream.java:298)
    at java.util.zip.ZipInputStream.readLOC(ZipInputStream.java:237)
    at java.util.zip.ZipInputStream.getNextEntry(ZipInputStream.java:73)
    …

例によって非 iso-8859-x 圏の人間を蹂躙した仕様というか何というか… とりあえずこれを回避してみよう。

◆ Ant 付属ユーティリティを使う

Apache の Ant (ant.jar) に付属している ZIP ユーティリティを使用することでこの問題を回避することが出来る。

Class ZipFile
This class adds support for file name encodings other than UTF-8 (which is required to work on ZIP files created by native zip tools and is able to skip a preamble like the one found in self extracting archives. Furthermore it returns instances of org.apache.tools.zip.ZipEntry instead of java.util.zip.ZipEntry.

API リファレンスを見る限り、正にこの問題のために用意されたクラスのようだ。Java 標準の ZipFile を継承せずフルスクラッチで書き直したと。まぁご苦労様でございます。では早速使ってみよう (ファイルクローズは省略)。

import org.apache.tools.zip.*;


ZipFile file = new ZipFile("zipfile.zip", "MS932");
Enumeration en = file.getEntries();
while(en.hasMoreElements()){
    ZipEntry entry = (ZipEntry)en.nextElement();
    if(entry.isDirectory()){
        // ディレクトリを検出した時の処理
    } else {
        InputStream in = file.getInputStream(entry);
        // ファイルを検出したときの処理
    }
}

メソッド名などに若干違いが見られるものの、使い方は標準の ZipFile とほぼ同じである。既にそちらを使い慣れているなら全く問題はないだろう。

ZipInputStream に相当するものは無いようだ。つまり、任意のストリームから読み込みながら展開、と言ったような芸当は出来ず、必ず一度ファイルに落とす必要がある。ストリーム処理を崇拝している人間としてはもどかしい限りだ。ちなみに setEncoding() を実装した ZipOutputStream はバンドルされている。

ところでこのクラス、Jakarta Commons ではなく Ant の方であることに注意されたし。きっと Ant の性質上、外部の圧縮ツールで作った ZIP ファイルを扱える必要があったんだろうね。個人的にはファイルパス部分だけを UTF-8 バイナリに置き換えるような FilterInputStream を作りたいところなんだが、これは ZIP ファイルのフォーマットを調べてから…

  • Apache Ant のダウンロード
  • BugParade
    関連する報告をざっと読む限り、日本は元よりドイツ・スペイン・中国・韓国とあらゆる国から悲鳴めいた要望が上がってます。

2007/12 追記: JDC などでも色々意見が出されていて直す気が出てきたようです ( 4244499参照)。

コメント
トラックバック
トラックバック URL
コメントの投稿
管理者にだけ表示を許可する
Profile
Takami Torao
Takami Torao
C/C++ 使いだった 1996年、運命の Java と出会い現在に至る。のらアーキテクト。
Yah, this is image so I don't wanna eat spam, sorry!
Search

Google
MOYO Laboratory
Web

カテゴリー
最近の記事
最近のコメント
最近のトラックバック
月別アーカイブ
ブロとも申請フォーム
RSSフィード
リンク
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。