Watch at Ease看見世界,讀懂生活。

科技 · 實用指南

ZIP 能列出檔案卻無法讀取?逐項檢查成員與 Python 解碼支援

四個受控 ZIP 樣本說明:目錄可讀、不支援壓縮方法與 CRC 錯誤,需要分別判斷。

原創示意圖:Python 3.12.14 下四個自製雙成員 ZIP 樣本,區分目錄可讀、不支援方法與 CRC 錯誤。
原創示意圖:Python 3.12.14 下四個自製雙成員 ZIP 樣本,區分目錄可讀、不支援方法與 CRC 錯誤。 Watch at Ease

若 zipfile 能列出壓縮檔,卻在讀取某個檔案時拋出 NotImplementedError,應檢查該成員及實際執行工作的 Python。目錄或較前面的成員可讀,並不能證明所有壓縮與加密特性都受支援。

我們以 Python 3.12.14 測試四個微型雙成員樣本;它們都能列出檔案並讀出 readme.txt,但 data.csv 的結果分成成功、不支援方法的例外與 CRC 錯誤。這些結果對應不同的排查方向。

記錄實際讀取器與每個成員

先在發生錯誤的程序記錄 sys.version 與 zipfile.__file__,再以唯讀方式開啟工作副本,走訪 z.infolist(),記錄每個成員的 filename、compress_type、flag_bits、file_size 及 compress_size。排程、執行環境或同名模組遮蔽,都可能使實際讀取器與另一個環境中檢查的不同。

這只是清單檢查,尚未讀取有效負載。不要以第一個成員的方法代表全部成員,也不要把 is_zipfile() 成功視為所有內容皆可讀。PKWARE 方法表另外區分 Deflate64(9)與 AE-x 加密標記(99);處理 Zstandard 支援不等於解決這些分支。

分開記錄三種測試結果

每個樣本先放入以儲存方式保存的 readme.txt,再放入相同的 24 位元組 data.csv。儲存加 Deflate(0/8)會回傳預期 CSV,testzip() 回傳 None;儲存加 Zstandard(0/93)與舊 Zstandard 編號(0/20)均可讀取說明檔,卻在讀取 CSV 時拋出 NotImplementedError,testzip() 也向外拋出相同例外。

第四個樣本使用 Deflate,但刻意寫錯 CRC 中繼資料。讀取 data.csv 拋出 BadZipFile,testzip() 則回傳檔名。應區分「拋出例外」「回傳壞檔名」與 None:解碼方法不受支援時,CRC 尚未驗證,不能據此判斷內容損毀或完好。

核對內建支援,不能只看是否安裝套件

已安裝的 zstandard 0.25.0 能獨立解壓我們製作的 Zstandard 資料框架,但匯入它不會替 Python 3.12.14 的 zipfile 加入 ZIP_ZSTANDARD。獨立編解碼函式庫可用,不表示 ZIP 讀取器會呼叫它;應核對實際直譯器、模組與該組建的文件能力。

Python 3.14 參考文件指出 ZIP_ZSTANDARD 在 3.14 加入,依賴 compression.zstd,讀取支援方法編號 20、93,寫入使用 93。這是特定版本的文件說明,並非本文對 3.14 環境的實測,也不保證升級後可處理所有壓縮、加密或封存特性。

選擇相容路徑,再驗證必要內容

依每個必要成員的壓縮與加密特性,核對具體讀取器的文件支援。必要時改用文件明確支援的讀取器,或請提供方另匯出相容副本,同時保留原件。不要修改標頭的方法編號,也不要降低加密強度來消除錯誤。Apache ZIP 指南已說明逐成員能力檢查;既有方法 9、99 的討論各自對應不同特性。

接著在有適當限制的環境讀取所有必要成員,核對 CRC 結果並驗證應用所需內容。本文四個微型未加密樣本的 12 項斷言均通過,未執行解壓寫入磁碟;這些測試不證明不可信封存檔的安全性,也不涵蓋 ZIP64、分卷或所有 Python 組建。所連結的通用 ZIP 指南解釋結構、CRC 與必要內容的獨立驗證步驟。

原創示意圖:先核對實際執行環境、每個必要成員與讀取器文件能力,再驗證有效負載和應用內容。
原創示意圖:先核對實際執行環境、每個必要成員與讀取器文件能力,再驗證有效負載和應用內容。 Watch at Ease