手軽屋
ツール一覧

ZIPフォーマット完全理解(ローカルヘッダ・セントラルディレクトリ・ZIP64)

PKWARE APPNOTE / MDN DataView / IETF RFC 1951 DEFLATE に基づく構造解説。

ZIPの全体構造

ZIPファイルは3つの主要セクションから成ります:①各ファイルのローカルファイルヘッダ+圧縮データの連続、②末尾のセントラルディレクトリ(目次)、③その後ろのEnd of Central Directory Record(EOCD)。解凍ツールは末尾からEOCDを探し、セントラルディレクトリを読み、各エントリの位置を特定します。

マジックナンバー(署名)

「PK」はPhil Katz(PKWARE創業者)のイニシャル。ZIPの父への永続的なクレジットです。

ローカルファイルヘッダの構造

各エントリのヘッダは固定30バイト+可変長部分。MDN DataView で読みます:

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フォーマットは現在も世界中で使われ続けています。

関連