ByteScope

EPUB 檔案格式

EPUB 電子書——一個裝著 XHTML 內容與資源清單的 ZIP 壓縮檔。

MIME 類型
application/epub+zip
副檔名
.epub
分類
文件

Magic number(檔案簽章)

0x00000000504B0304PK··'PK' — it is a ZIP

ZIP 內含一個 'mimetype' 項目,內容為 'application/epub+zip'。

關於 EPUB 格式

EPUB 是開放的電子書標準,結構上就是塞在 ZIP 裡的一個網站:可重排的 XHTML 文件、CSS、字型與圖片,加上一份列出所有資源與閱讀順序的套件文件。和其他所有以 ZIP 為基礎的格式不同,EPUB 的容器規格把壓縮檔開頭那幾個位元組釘得死死的,所以它是少數能光靠標頭辨識、不必看目錄清單的容器格式。這是刻意的設計,為的是讓伺服器或偵測器不用解壓縮就認得出型別。

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

位移長度欄位意義
04Local file header signature50 4B 03 04。EPUB 就是普通的 ZIP 壓縮檔;底下這些都是它第一個項目的 ZIP 本地檔案標頭,只是 OCF 規格對這些欄位的約束比 ZIP 本身嚴格得多。
42Version needed to extract0A 00,也就是 10,因為第一個項目必須是直接儲存而不是 deflate,而直接儲存的項目只需要 1.0 版。這裡看到 14 00 代表寫入端把 mimetype 項目壓縮了,這個套件已經不符規格。
62General purpose bit flag00 00。mimetype 項目不得使用 data descriptor,所以位元 3 是 0,底下的 CRC 與大小都是真實數值而不是零。整個標頭之所以能是確定的,關鍵就在這裡。
82Compression method00 00——直接儲存、未壓縮。規格明文要求,用意正是讓媒體型別字串以可讀 ASCII 出現在檔案的固定位置,而不是躲在 deflate 資料流裡。
102Last modified timeMS-DOS 打包的時間。這是規格唯一放任不管的欄位,所以它會隨檔案不同而變,周圍的東西則不會。
122Last modified dateMS-DOS 打包的日期,年份從 1980 起算。OCF 同樣沒有約束,資訊量同樣是零——這本書真正的日期在套件文件的中繼資料裡。
144CRC-326F 61 AB 2C,也就是小端序的 0x2CAB616F,每一個符合規格的 EPUB 都是這個值——因為項目內容是固定的,檢查碼自然是常數。這裡出現別的值,代表 mimetype 檔的內容不是預期的那 20 個位元組。
184Compressed size14 00 00 00,20 個位元組。因為項目是直接儲存,所以和未壓縮大小相等。
224Uncompressed size14 00 00 00,也就是 20——字串 application/epub+zip 的位元組長度,結尾不能有換行。這裡多一個換行的檔案會驗證失敗,而原因在文字編輯器裡完全看不出來。
262File name length08 00,8 個位元組,對應名稱 'mimetype'。規格釘死的,要求就是這個全小寫的 ASCII 名稱。
282Extra field length00 00。規格禁止這個項目帶額外欄位,這正是媒體型別字串保證落在位移 38、而不是更後面某處的原因。
308Entry nameASCII 的 'mimetype'。它必須是壓縮檔的第一個項目,所以前面不能有任何其他檔案。
3820Media type string字面的 ASCII application/epub+zip。要讓它出現在這裡,得同時滿足六個獨立條件——直接儲存、沒有額外欄位、沒有 data descriptor、名稱一字不差、長度一字不差、排在壓縮檔第一位——這讓位移 38 那 20 個位元組成為比位移 0 的 'PK' 強得多的識別依據。
variablevariableMETA-INF/container.xml第二個必備項目,不過它不在固定位移。它是讀取端唯一事先知道位置的檔案,存在的唯一目的就是指向套件文件——通常是 OEBPS/content.opf——套件文件再列出 manifest 與 spine,也就是資源清單與有序的閱讀順序。
-224End of central directory signature50 4B 05 06,在沒有壓縮檔註解時位於倒數第 22 個位元組。就算開頭釘得再死,EPUB 仍然是 ZIP:讀取端是從檔尾的中央目錄列舉資源的,所以被截斷的檔案打不開,即使它前 58 個位元組完美無瑕。

怎麼跟長得像的格式分開

容易搞混的怎麼分
ZIP每個 EPUB 都是 ZIP,但幾乎沒有 ZIP 是 EPUB。這個測試強度異常地高,因為 OCF 把整個第一項目都釘死了:位移 8 是方法 0、位移 26 名稱長度是 8、位移 28 額外欄位長度是 0、位移 30 是 'mimetype'、位移 38 是 application/epub+zip,加上位移 14 的常數 CRC 0x2CAB616F。一般用途的壓縮檔不可能六項全中。
Word 文件(docx)兩者都是裝 XML 的 ZIP 套件,也都會讓只看到 'PK' 就收工的粗糙偵測器翻車。OOXML 在位元組層級什麼都沒釘,靠的是套件根目錄出現 [Content_Types].xml——通常在位移 30;EPUB 則把整個第一項目都釘死。位移 30 讀到 'mimetype' 就是 EPUB,讀到 '[Content_Types].xml' 就是 OOXML。
MOBI/AZW亞馬遜較早的電子書格式,而且根本不是 ZIP:那是從 PalmDOC 衍生出來的容器,位移 60 有 ASCII 字串 'BOOKMOBI';KF8/AZW3 則是包著同一個容器。Kindle 裝置讀的是那些,而且直到相對近期都完全不讀 EPUB,所以同一本書常常兩種格式都發。
PDF兩種電子書交付格式,設計上正好相反。PDF 把每個字元釘在固定頁面的座標上;EPUB 是會依螢幕與讀者選的字級重排的 XHTML。這就是為什麼「到哪都要長一樣」的文件該用 PDF、而 PDF 在手機上很糟,EPUB 則反過來。

常見的壞法

  • mimetype 項目被壓縮或被移位。弄壞 EPUB 最常見的方式,就是用普通的 zip 指令重新打包,它會把每個項目都 deflate,還可能重排順序。結果是一個完全合法的 ZIP,而閱讀器拒收,因為媒體型別字串不再是位移 38 的明碼 ASCII。要正確打包,得先把 mimetype 以不壓縮的方式加進去,其他東西再跟上。
  • 壓縮檔尾巴不見了。前 58 個位元組把檔案身分交代得漂漂亮亮,書還是打不開,因為閱讀器是從檔尾的中央目錄列舉資源的。從 50 4B 03 04 往後切出各項目、重建目錄,XHTML 能完整救回。
  • META-INF/container.xml 不存在,或它指向的套件文件不在。壓縮檔裡其他東西可以完好無缺——章節、圖片、樣式表——閱讀器卻找不到入口,於是整本書看起來是空的。這是指標壞掉而不是資料壞掉,手動就修得回來。
  • manifest 和壓縮檔內容對不起來。每個資源都必須在套件文件裡宣告;ZIP 裡有、manifest 裡沒有的章節,就是永遠不會出現,而 manifest 裡有、卻找不到對應檔案的項目,會讓嚴格的閱讀器整本書都拒收。這兩種在任何 ZIP 工具眼裡都不算損壞。
  • 文字模式傳輸。換行轉換讓每個固定標頭欄位照樣讀得出來——mimetype 是直接儲存的,所以它真的活得下來——同時悄悄破壞各章節的 deflate 資料流。書被正確辨識、也打得開,然後讀到一半 CRC 不符。

這個格式現在還在用嗎

除了亞馬遜的生態系之外,EPUB 就是各處的電子書標準:圖書館外借的是它、出版社交付的是它、Apple Books、Kobo、Google Play Books 和每個開源閱讀器吃的都是它,而 2022 年起 Kindle 的個人文件也收它了。第 3 版讓它更貼近它本來就依附的網頁技術——HTML5、CSS3、內嵌字型、MathML、可跟讀的媒體疊加音訊,以及足以做互動教科書的指令碼能力。它的定義性特質是重排:文字會順應任何螢幕尺寸與讀者選的字級,這正是手機上讀小說需要的,也正是 PDF 做不到的。同一個特質也讓它不適合任何「版面本身帶有意義」的東西——有精確圖表的技術書、樂譜、漫畫和送印稿還是走 PDF,而 EPUB 的固定版面設定檔比較像是權宜之計,不算它的強項。把 EPUB 當一般 ZIP 對待之前,有一件事要先知道:用普通的 zip 指令重新打包會把它弄壞,因為第一個項目的儲存方式是寫在規格裡的,不是實作細節。

在這個站上打開 EPUB 檔

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

相關格式

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

格式開頭位元組副檔名
ZIP50 4B 03 04.zip
Word 文件(docx)50 4B 03 04.docx
HTML無固定簽章.html

常見問題

檔案的 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 仍然可能夾帶惡意內容。辨識真實類型是有用的第一道檢查,尤其在副檔名和位元組不一致的時候,但它不是惡意軟體掃描。

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