[C#] ZipArchiveにはCRCチェックがない?

  • 投稿日:
  • by
  • Category:

C#で作成したアプリケーションで起きた話。

ZIPファイルを展開して中のファイルのバイナリ読み込むアプリケーションを作成した。

サンプルコードはこんな感じ。

using (ZipArchive archive = ZipFile.OpenRead(zipPath))
{
    foreach (ZipArchiveEntry entry in archive.Entries)
    {
        using (Stream entryStream = entry.Open()) {
                //StreamをMemoryStreamに変換してデータを取り出す
        }
    }
}

ZIPファイルをZipArchiveクラスとして開くとZipArchive.EntriesプロパティでZIPファイル内の各ファイルにアクセスできる。

あとは各ファイルをOpenすればStream形式で取り出せるが、このStreamはSeekできない(データの途中から読みだせない)ため非常に扱いづらい。

MemoryStreamに変換すれば色々と便利に扱えるため、CopyToしてからbyte[]に切り出すことにした。

 

 

よくよく調べてみたら、ZipArchiveファイルを開くときZipArchiveMode.Updateで開けばSeek-ableになるらしい。

For read only / write only you get a wrapped DeflateStream which doesn't tell you the length of the stream nor permit you to seek it. For read/write (update) ZipArchiveEntry will read and decompress the entire entry into memory (in fact, into a memory stream backed by a single contiguous managed array) so that you have a seek-able representation of the file.

For an archive opened with ZipArchiveMode.Update we could allow FileAccess.Read, FileAccess.Write, or FileAccess.ReadWrite, where only the latter would do the MemoryStream expansion.

わざわざMemoryStreamにコピーしなくても良かったのかも。でもこのあと出てくる.ToArray()が使えないからやっぱりダメか。

 

破損ファイルがそのまま読めてしまう

一通り完成したためテストしていたら、バイナリエディタで壊した(CRCチェックサムが一致しない)ZIPファイルが何とそのままOpenで読めてしまった。普通ファイルオープンエラーとかにならん?

試しに取り出したStreamのサイズを確認するとファイルサイズと一致。サイズ的には正常に読み出せてしまっている。

.ToArray()でbyte[]に変換してバイナリ値を確認すると、壊れた個所から後は全部ゼロに化けてしまっていた。エラー出してお願い

 

そういえば確かにZIPファイルって壊れててもファイル一覧までは見れるよなぁ(展開しようとするとCRCエラーが出るけど)。

これはどうやらZipArchiveでZIPファイルを展開する場合にはCRCチェックが行われないように見える。困るぞ...。

※ちなみにZIP作成時もきちんとDisposeしないとCRC値が書き込まれないらしい。IDisposableインターフェイスを実装するクラスを使う場合はusingを使って確実にDisposeしよう。

 

CRCチェックを実装したい

となると何とかCRCチェック機能を実装したい。

ググってみるとどうやらZiparchiveEntryは内部値としてcrc32値を持っているらしいが、intenalの為アクセスできず。

Reflectionで無理やりアクセスすることはできたが、ファイルの実CRCを計算して照合する機能も自力で実装するのは手間がかかりすぎるので断念。

 

結局SharpZipLibを頼った

できるだけOSSライブラリを増やしたくはなかったが、全部自分で実装するのも大変すぎたのでSharpZipLibを導入した。

これが神レベルの便利さ。ZipFile.TestArchiveメソッドで簡単にCRCチェックを行うことができた。

 

結論

ZIP扱うならSharpZipLib必須。最高。ライセンスもMITライセンスなので非常に寛容。

DB扱う時のDapperと同じくらい、これ無しでは生きていけない。

コメントする