技術関係

「ZipInputStream」がWindowsで作成したzipファイルを読み込めない

「ZipInputStream」を使ってzipファイル読み込み処理を実装してます。

すると、特定のファイルだけ何故か読み込むことが出来ない現象にぶち当たりました。
そのファイルとは・・・

 

「Windows環境で作成した日本語名ディレクトリのファイル」

 

普段からMacを使っていると中々気付きにくい点で厄介でした。

このファイルをどうにかして読み込ませたい・・・。

スポンサーリンク
スポンサーリンク

要件

  • 英数と日本語名で渡されるどちらのケースにも対応。
  • ディレクトリ名自体は後の処理で使用しない。
  • とりあえずディレクトリ内のファイルを読み込む事が出来ればOK。

調べてみると、「ZipInputStream」で文字コードを指定して対応させることで対処出来たのでメモとして書いていきます。

環境情報

  • Java: 1.8.0_172
  • Kotlin: 1.2.50-release-103

原因は ZipInputStream

Windowsで作成したマルチバイト名のディレクトリだけ読み込みません。

早速読み込み失敗時のエラーを見てみます。

java.lang.IllegalArgumentException: MALFORMED
    at java.util.zip.ZipCoder.toString(ZipCoder.java:58) ~[na:1.8.0_172]
    at java.util.zip.ZipInputStream.readLOC(ZipInputStream.java:300) ~[na:1.8.0_172]
    at java.util.zip.ZipInputStream.getNextEntry(ZipInputStream.java:122) ~[na:1.8.0_172]

// 以下略

「MALFORMED」とあります。

ググってみると、「奇形の」「不正な形式の」とあります。

どうやら渡したファイルが不正なデータ扱いされているようです。

早速、実装コードの中を確認してみます。

Kotlin での実装を見てみる。

問題の実装はこんな感じ。

it.nextEntryを呼んだ所で例外が発生しています。

ZipInputStream(file.inputStream).use { input ->
    val entry = input.nextEntry ?: break
// 略

 

では、次に「ZipInputStream」の中を見てみます。

public ZipInputStream(InputStream in) {
    this(in, StandardCharsets.UTF_8);
// 略

原因はここでした。

 

どうやら何も指定しないとデフォルトの文字コードは「UTF-8」になっています。

ここに「UTF-8」以外の文字コード名のファイルを渡しても、当然予期していない文字コードなので読み込むことが出来ずエラーを吐きます。

 

ちなみにWindows環境で作成したディレクトリは「MS932」という文字コードになります。

「Windows = Shift-JIS」だと思っていましたが、そうではないらしいです。

「MS932」は「Windows-31J」という名称とも同義で、Javaの世界では「MS932」を使用して区別しているそうです。

Shift_JIS と Windows-31J (MS932) の違いを整理してみよう / WEB ARCH LABO

解決したKotlinのコード

原因は

  • 「MS932」の文字コードのディレクトリを「UTF-8」として読み込もうとしていたから

ということで、こういうコードになりました。

val CHARSET = listOf("UTF-8", "MS932").map(::charset)

CHARSET.forEach {
    try {
        ZipInputStream(file.inputStream, it).use { input ->
            val entry = input.nextEntry ?: break
            // 略
        }
        return
    } catch (e: IllegalArgumentException) {
        // ログ出力
    }
}

定数で 定義した「文字コードのリスト」をforEachで回しています。

.mapStringからCharsetに変換しています。

 

try-catchで失敗した場合はログを出力し、ループ自体は続行。

成功した場合はreturnでループを離脱。

 

これでUTF-8で読み込みが失敗した場合、MS932を用いて読み込みを行うことが出来ました。

 

おわり!

参考サイト

Java Zipファイルメモ(Hishidama’s java zip Memo) – Ne

ZipInputStream(InputStream, Charset) decodes ZipEntry file name falsely / stackoverflow

Android Nougat以降で日本語フォルダ名を含むZipFile / なーんだ、ただの水たまりじゃないか

Shift_JIS と Windows-31J (MS932) の違いを整理してみよう / WEB ARCH LABO

コメント

タイトルとURLをコピーしました