.NETの System.Io.Compression で非圧縮のZIPファイルを作れなかったが対象が固定だったので直接バイナリファイル作って強引に解決した話

この話は件名が全てなので、タイトルを読んで「ああ、そうですね」と思った人は読んでも新たな知見は得られません

EPUBファイルを作るためにmimetypeを非圧縮でZIP化したいが System.Io.Compression で上手く出来ない

EPUBの仕様ではmimetyeファイルは非圧縮でzipファイルの最初に格納されていなければなりません

さらに mimetype ファイルは、圧縮または暗号化をしてはならず(must)、そして ZIP ヘッダにエキストラフィールドがあってはならない(must not)。

3.3 OCF ZIP コンテナのメディアタイプの識別 - PUB Open Container Format (OCF) 3.0.1(日本語訳版)

そこで .NETSystem.Io.Compression 名前空間を使ってみます

ZipFileExtensionsCreateEntryFromFile メソッドに [CompressionLevel]::NoCompression を指定して圧縮するPowerShellを書いてみます

using namespace System.IO.Compression;

[ZipArchive] $zipArchive = [ZipFile]::Open('sample.epub',[ZipArchiveMode]::Update);

[ZipFileExtensions]::CreateEntryFromFile($zipArchive, 'mimetype', 'mimetype', [CompressionLevel]::NoCompression) > $null;

$zipArchive.Dispose();

しかし、確認してみると mimetype ファイルは Deflate で圧縮されてしまっています

駄目じゃん

と言う訳で圧縮済みのZIPをバイナリファイルとして直接作ることにした

ZIP化の対象ファイルが可変の内容であれば別なツールを使うか、最悪 ZIP File Format Specification を読む羽目になっていたところですが、今回対象としているEPUBmimetye ファイルは幸い内容が完全固定で、しかも小さいです

つまり、最初からZIP化済みのファイルを用意しておいて複製して使ったり、或いは固定のZIP化したバイナリィデータを知っておけばそれを書き込んで直接ZIPファイルを生成したりして今回はこの問題を回避できます

てな訳で、以下のコードで問題回避

[Byte[]] $byteData = ('80', '75', '3', '4', '10', '0', '0', '0', '0', '0', '99', '140', '53', '77', '111', '97', '171', '44', '20', '0', '0', '0', '20', '0', '0', '0', '8', '0', '0', '0', '109', '105', '109', '101', '116', '121', '112', '101', '97', '112', '112', '108', '105', '99', '97', '116', '105', '111', '110', '47', '101', '112', '117', '98', '43', '122', '105', '112', '80', '75', '1', '2', '30', '3', '10', '0', '0', '0', '0', '0', '99', '140', '53', '77', '111', '97', '171', '44', '20', '0', '0', '0', '20', '0', '0', '0', '8', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '255', '129', '0', '0', '0', '0', '109', '105', '109', '101', '116', '121', '112', '101', '80', '75', '5', '6', '0', '0', '0', '0', '1', '0', '1', '0', '54', '0', '0', '0', '58', '0', '0', '0', '0', '0');

[System.IO.File]::WriteAllBytes("./sample.epub", $byteData);

バイナリファイルの保存はWindows PowerShellなら Set-Content './sample.epub' -Value $byteData -Encoding Byte;PowerShell Coreなら Set-Content -Path './sample.epub' -Value $byteData -AsByteStream; でも行ける感じです

我ながら強引ですが、そんな感じで一つ