ByteScope

GZIP 檔案格式

gzip 壓縮的資料,通常包的是單一檔案或一個 tar 封存檔。

MIME 類型
application/gzip
副檔名
.gz
分類
壓縮與封存

Magic number(檔案簽章)

0x000000001F8B··gzip magic

關於 GZIP 格式

gzip 用 DEFLATE 壓縮單一資料流,是 HTTP 壓縮的支柱,也是到處都看得到的 .tar.gz 的真面目。檔案以 2 個 magic 位元組 1F 8B 開頭。這個格式刻意做得很小 — 十個位元組的固定標頭、幾個選用欄位、deflate 串流,加上八個位元組的結尾 — 也刻意可以串流:開始壓縮之前不需要先知道任何東西,結束時也不需要倒回去補任何東西。而這同時就是它的限制。它沒有目錄、沒有逐檔結構,也沒有辦法在不解壓縮前面所有資料的情況下讀到中間。

檔頭結構,一個位元組一個位元組看

位移長度欄位意義
02Magic ID1 ID21F 8B。兩個位元組都不是可列印的 ASCII,這是刻意的:gzip 串流絕不能讓郵件閘道或某個想幫忙的終端機看起來像文字。
21Compression method (CM)8,代表 deflate。0 到 7 是保留值,從來沒有一個被指派過,所以實務上這裡只要不是 08,不是這個檔案不是 gzip,就是標頭壞了。
31Flags (FLG)位元 0 FTEXT(內容很可能是文字)、位元 1 FHCRC(後面跟著兩個位元組的標頭 CRC)、位元 2 FEXTRA、位元 3 FNAME(後面跟著一個以 NUL 結尾的原始檔名)、位元 4 FCOMMENT。用命令列工具壓出來的檔案通常讀到 08,因為它會存檔名。
44Modification time (MTIME)原始檔案的 Unix 秒數,小端序,取不到時就是 0。同樣的輸入壓兩次會得到不同的位元組就是因為它,而可重現建置也是為此加旗標把它歸零。
81Extra flags (XFL)配上 deflate 時,2 代表用了最慢也最好的壓縮,4 代表用了最快的;其他情況是 0。它純粹是參考用 — 沒有任何解壓縮器會拿它做事。
91Operating system (OS)原始檔案來自哪種檔案系統:0 是 FAT、3 是 Unix、11 是 NTFS、255 是未知。它會洩漏檔案是在哪裡建置的,這也是可重現建置會去正規化的另一個欄位。
10variableOptional fields, then the deflate stream被設起來的那些旗標對應的欄位,順序固定:額外欄位(兩個位元組的長度加那麼多位元組)、原始檔名、註解、標頭 CRC-16。一個存著檔名 'payload.bin' 的檔案,位移 10 到 21 花在它身上,deflate 從 22 開始。
-84CRC-32 of the uncompressed data小端序,算的是原始位元組、不是壓縮後的位元組。`gzip -t` 檢查的就是它,而被截斷的檔案永遠滿足不了它。
-44ISIZE原始大小對 2^32 取模,小端序。200000 位元組的輸入結尾是 40 0D 03 00。因為它是 32 位元的,超過四 GB 就會悄悄繞回去 — 這就是為什麼 `gzip -l` 對超大檔案報出來的是胡說八道,也是為什麼絕對不能拿它當配置緩衝區的大小。

怎麼跟長得像的格式分開

容易搞混的怎麼分
zlib這一對才是真的會坑到人,因為兩者包的是同一條 deflate 串流。zlib 串流的標頭只有兩個位元組、不是十個 — 實務上是 78 01、78 9C 或 78 DA — 而且結尾是 Adler-32 而不是 CRC-32。它沒有 magic number、沒有檔名也沒有長度,所以一個被改名成 .gz 的 zlib 區塊哪裡都打不開,而把 gzip 餵給 zlib 解碼器則會當場失敗。
raw deflateRFC 1951 定義的裸 deflate 完全沒有標頭也沒有結尾 — 它直接從第一個區塊的三個位元開始。ZIP 項目存的就是它,而 HTTP 那個曖昧的 `Content-Encoding: deflate` 講的也是它 — 這也是為什麼那麼多 HTTP 用戶端會先試 zlib、失敗再退回裸的。
TAR一個 .tar.gz 和「壓了一個普通檔案的 gzip」是同一種格式,差別只在內容。解壓縮之後看結果的位移 257 — tar 在那裡有 'ustar',位在第一個 512 位元組成員標頭裡面。更省事的是位移 10 的 FNAME 欄位,它通常會以 .tar 結尾,自己招了。
ZIP1F 8B 對上 50 4B 03 04,而且結構上比「壓縮」這個共通詞暗示的差得更遠:ZIP 檔尾有一份中央目錄,可以在不碰其他部分的情況下列出並抽出單一項目,而 gzip 串流要讀到任何東西都得從頭開始。
BZIP2'BZh' 加一個數字對上 1F 8B。兩者在 Unix 上的用法一模一樣,在建置腳本裡幾乎被當成同義詞,所以副檔名常常是錯的;前兩個位元組則從來不會錯。

常見的壞法

  • 下載被截斷。八個位元組的結尾不見了,所以解壓縮器會把它解得出來的全部吐出來,然後回報非預期的檔案結束。救回來的位元組通常是好的 — 問題在於沒有東西能確認這件事,因為長度和檢查碼都住在那段消失的尾巴裡。
  • deflate 串流裡有一個位元被翻掉。輸出長度是對的,而位移 -8 的 CRC-32 對不上,這正是 `gzip -t` 存在要指出的情況。看那個位元落在哪裡,解壓縮也可能根本走不到檢查碼那一步,就先因為不合法的 distance 或碼長錯誤而整個失敗。
  • 文字模式傳輸。換行轉換把壓縮資料裡的 0D 0A 改寫成 0A,而十個位元組的標頭原封不動,所以這個檔案照樣被認成 gzip、照樣報得出原始檔名,然後在串流中途失敗。
  • 多成員的封存檔被當成一個來讀。把 gzip 檔案串接起來是合法的,合規的解壓縮器會把它們全部接著吐出來,但一個自己手寫、讀到第一個 ISIZE 就停的剖析器,會靜靜地把第一個成員之後的東西全部丟掉。平行壓縮器產出的正是這種形狀。
  • 把 ISIZE 當成大小來相信。超過四 GB 它就繞回去,所以拿它去配置緩衝區的程式,會少配整整幾個 4 GiB。它是一個描述資料的標頭欄位,不是一個承諾,而且沒有任何檢查碼保護它。

這個格式現在還在用嗎

gzip 是全世界被執行次數最多的壓縮格式,而且看不出有要退場的跡象。每個 HTTP 技術棧都還在協商它,.tar.gz 是 Unix 上原始碼發佈的預設形狀,log 輪替寫的是它,而每種語言的標準函式庫都內建一個解碼器 — 所以只要不確定讀取端是誰,它就是那個安全答案。壓縮率上它早就輸了:bzip2、xz 和 Zstandard 全都贏它,而在網頁上,靜態文字是 Brotli 贏,相近壓縮率下的速度則是 Zstandard 贏。讓 gzip 保住第一選擇的是通用性加上可串流:它不需要 seek、不需要暫存檔,也不需要事先知道輸入長度,所以在管線裡、在 socket 裡、在韌體映像檔裡都能用。而只要你需要抽出一堆檔案裡的某一個,它就是錯的選擇,因為它沒有索引 — 那是 ZIP 的工作。

在這個站上打開 GZIP 檔

這些全部在你的瀏覽器裡跑,檔案不會離開你的電腦。

相關格式

容易和 GZIP 搞混的格式,可以比對它們的開頭位元組。

格式開頭位元組副檔名
TAR75 73 74 61 72.tar
ZIP50 4B 03 04.zip
BZIP242 5A 68.bz2
XZFD 37 7A 58 5A 00.xz
Zstandard28 B5 2F FD.zst

常見問題

檔案的 magic number 是什麼?

檔案開頭的前幾個位元組,不管檔名寫什麼,它標明的是真正的格式。PNG 一定以 89 50 4E 47 開頭,ZIP 則是 50 4B 03 04。作業系統、瀏覽器和 file 指令讀的都是這幾個位元組,而不是相信副檔名 — 這就是為什麼把檔案改名並不會改變它實際上是什麼。

我要怎麼自己檢查 magic number?

在 macOS 或 Linux 上,xxd -l 16 檔名 會印出前 16 個位元組;在 Windows 上,PowerShell 的 Format-Hex -Path 檔名 -Count 16。或者把檔案丟進本站的檔案類型辨識工具,它會在本機讀取位元組並告訴你是什麼格式,不會上傳任何東西。

為什麼兩種格式的 magic number 會一樣?

因為其中一種是建立在另一種之上。docx、xlsx、pptx、apk、jar 與 epub 骨子裡都是 ZIP 壓縮檔,所以全都以 ZIP 的簽章開頭;要分辨它們得看裡面的項目。Java class 檔和 macOS 的通用二進位檔都以 CA FE BA BE 開頭,靠緊接其後的欄位區分。

有完全沒有 magic number 的檔案嗎?

有。XML、HTML、JSON、SVG、CSV 這類以文字為基礎的格式沒有固定的位元組簽章,所以改用結構來辨識:開頭的 <?xml 宣告、<svg 元素,或是 { 後面接著帶引號的鍵。這種偵測是合理推測而非確定,因此本參考頁會把這些格式標示為結構偵測。

magic number 相符就代表檔案安全嗎?

不代表。它告訴你的是格式,不是內容是否無害 — 一個貨真價實的 PDF 或 docx 仍然可能夾帶惡意內容。辨識真實類型是有用的第一道檢查,尤其在副檔名和位元組不一致的時候,但它不是惡意軟體掃描。

手上有檔案可能是 GZIP?用檔案類型辨識工具查出來 — 它在本機讀取 magic number,不上傳任何東西。或者瀏覽所有格式。