.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(日本語訳版)
そこで .NET の System.Io.Compression
名前空間を使ってみます
- ZipFileExtensions.CreateEntryFromFile Method (System.IO.Compression) | Microsoft Docs
- CompressionLevel Enum (System.IO.Compression) | Microsoft Docs
ZipFileExtensions
の CreateEntryFromFile
メソッドに [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 を読む羽目になっていたところですが、今回対象としているEPUBの mimetye
ファイルは幸い内容が完全固定で、しかも小さいです
つまり、最初から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;
でも行ける感じです
我ながら強引ですが、そんな感じで一つ