ZIPフォーマット完全理解(ローカルヘッダ・セントラルディレクトリ・ZIP64)
PKWARE APPNOTE / MDN DataView / IETF RFC 1951 DEFLATE に基づく構造解説。
ZIPの全体構造
ZIPファイルは3つの主要セクションから成ります:①各ファイルのローカルファイルヘッダ+圧縮データの連続、②末尾のセントラルディレクトリ(目次)、③その後ろのEnd of Central Directory Record(EOCD)。解凍ツールは末尾からEOCDを探し、セントラルディレクトリを読み、各エントリの位置を特定します。
マジックナンバー(署名)
- 0x04034b50("PK\x03\x04"):ローカルファイルヘッダ
- 0x02014b50("PK\x01\x02"):セントラルディレクトリエントリ
- 0x06054b50("PK\x05\x06"):EOCD(末尾レコード)
- 0x06064b50:ZIP64版EOCD
「PK」はPhil Katz(PKWARE創業者)のイニシャル。ZIPの父への永続的なクレジットです。
ローカルファイルヘッダの構造
各エントリのヘッダは固定30バイト+可変長部分。MDN DataView で読みます:
- offset 0:署名(4バイト・0x04034b50)
- offset 4:解凍に必要な最小version(2バイト)
- offset 6:general purpose bit flag(2バイト・bit 0=暗号化、bit 11=UTF-8)
- offset 8:圧縮方式(2バイト・0=無圧縮、8=DEFLATE)
- offset 10-13:最終更新日時(DOS time/date 2バイト×2)
- offset 14:CRC-32(4バイト)
- offset 18:圧縮サイズ(4バイト・ZIP64では拡張へ)
- offset 22:非圧縮サイズ(4バイト)
- offset 26:ファイル名長(2バイト)
- offset 28:extra field長(2バイト)
- offset 30以降:ファイル名/extra field/圧縮データ本体
bit 11(Language Encoding Flag)
2006年APPNOTE v6.3.0で追加されたEFS(Efficient Files Storage flag)。general purpose bit flagのbit 11が立っていれば、ファイル名はUTF-8で符号化されています。立っていなければ、Windowsの場合Shift_JIS(日本)/CP-437(米国)と推測する必要があります。
ZIP64拡張(4GiB→16EiB)
1996年仕様v4.5で追加。ローカルヘッダの圧縮サイズフィールドが32ビット(最大4GiB)では足りない場合、extra fieldにZIP64 extended information(Header ID 0x0001)を追加し、64ビット(最大16EiB)でサイズを記録します。
末尾もZIP64 EOCD Record(0x06064b50)+ZIP64 EOCD Locator(0x07064b50)を通常のEOCDの前に置きます。読み取り時はEOCDを見つけたあとZIP64 Locatorを探す順序です。
DataViewで実装する例
ZIPはリトルエンディアンなので、getUint32(offset, true) の第2引数をtrueにします:
const dv = new DataView(buffer); const sig = dv.getUint32(0, true); // 0x04034b50 const gpFlag = dv.getUint16(6, true); // bit 11 check const isUtf8 = (gpFlag & 0x0800) !== 0; const fnLen = dv.getUint16(26, true); const exLen = dv.getUint16(28, true); const fnBytes = new Uint8Array(buffer, 30, fnLen); const decoder = new TextDecoder(isUtf8 ? "utf-8" : "shift-jis"); const filename = decoder.decode(fnBytes);
DEFLATE展開
圧縮方式8=DEFLATEはIETF RFC 1951(1996年標準化)で定義されたLZ77+Huffman符号の組み合わせ。Webブラウザは DecompressionStream("deflate-raw")(Baseline 2024-03)で展開可能。本ツールもこのStream APIを利用して大きなZIPでもメモリ効率良く解凍します。
よくある質問
Q. なぜ末尾から読むのですか?
A. ストリーミング書き込みに対応するためです。書き手は最終的なファイルサイズが分かるまで目次を確定できないので、末尾にまとめて書きます。読み手は逆に末尾を見ればすぐ全体構造が分かります。
Q. CRC-32は何のために?
A. 圧縮データの破損検出です。32ビットのチェックサムで、ストレージ・通信中のビット反転を検知できます(暗号学的な改竄検出はできません)。
Q. PKZIPはなぜ「PK」なのですか?
A. 創業者Phil Katz(フィル・カッツ)のイニシャル。1989年にPKZIP 1.0としてリリース、ARCの後継として急速に普及しました。Katzは2000年に37歳で亡くなりましたが、ZIPフォーマットは現在も世界中で使われ続けています。