使用ZLib 壓縮/解壓縮 zip文件

實際應用中有時候會遇到需要處理 ZIP 壓縮解壓的情況,這時候我們有大概三種選擇:

  1. 調用 rar.exe, unzip.exe 等
  2. 使用某現成庫
  3. 完全手寫

第一種雖然能完成任務,但是沒法知曉結果。曾經有人對說,可以抓命令行輸出結果來判斷……這種依靠界面文字來進行精確判斷的行爲個人認爲相當不靠譜。第三種,既然我是個“造輪主義”者,當然說好,但是現在我不瞭解 ZIP 格式,也不瞭解 ZIP 算法,所以這個日後再說。今天我們就來切切實實地用一次輪子。

ZIP 相關的庫中比較有名的可能就是 ZLib 和 InfoZip(unzip60)了。InfoZip 我瞭解的不多,其外層接口似乎也不大好,一堆回調——回調是個很煩人的東西,專門用來打亂代碼結構。另外,這個庫也已經有好多年沒更新了吧,太久的東西給人的感覺總是不太舒服。ZLib 最新版本是 1.2.5,今年 4 月 19 日出的。確切的說,ZLib 可能並不是一個針對 ZIP 文件的庫,它只是一個針對 gzip 以及 deflate 算法的庫。它提供了一個叫做 minizip (contrib/minizip) 例子來給出操作 ZIP 文件的方法。下文將從 ZLib 出發,歸結出兩個傻瓜接口:

BOOL ZipCompress(LPCTSTR lpszSourceFiles, LPCTSTR lpszDestFile);
BOOL ZipExtract(LPCTSTR lpszSourceFile, LPCTSTR lpszDestFolder);

要引入的源文件

  1. ZLib 主目錄下的代碼,除 minigzip.c、example.c 外;
  2. contrib/minizip 下的代碼,除 minizip.c、miniunz.c 外。

相關 API

雖然 minizip 更像是個例子,但是除去其主程序 minizip.c 和 miniunz.c 後,剩下的部分我們可以看作是 ZLib 的一個上層庫,它封裝了與 ZIP 文件格式相關的操作。而 minizip.c 和 miniunz.c 就是我們要改寫的——把它從命令行程序改爲上述傻瓜接口。minizip.c 和 miniunz.c 中用到的 API 主要有:

壓縮相關:

  1. zipOpen64
  2. zipClose
  3. zipOpenNewFileInZip
  4. zipCloseFileInZip
  5. zipWriteInFileInZip

解壓相關:

  1. unzOpen64
  2. unzClose
  3. unzGetGlobalInfo64
  4. unzGoToNextFile
  5. unzGetCurrentFileInfo64
  6. unzOpenCurrentFile
  7. unzCloseCurrentFile
  8. unzReadCurrentFile

想必看到這些名字都能猜到怎麼用了吧。好的接口果然能帶給人愉悅的。minizip 中的這些函數有的是帶“64”的有的是不帶的,有的還有“2”、“3”、“4”版本。這裏一律用帶 64 的,不帶“2”、“3”、“4”的。

具體操作

下文涉及的所有操作,其相關代碼都可以在 http://zlibwrap.codeplex.com/ 上找到(Change Set 2450)。這裏就不貼長篇代碼了。另外有個 DLL版本 和 Lib版本,供拿來主義者用。

首先是壓縮操作。使用 zipOpen64 來打開/創建一個 ZIP 文件,然後開始遍歷要被放到壓縮包中去的文件。針對每個文件,先調用一次 zipOpenNewFileInZip,然後開始讀原始文件數據,使用 zipWriteInFileInZip 來寫入到 ZIP 文件中去。zipOpenNewFileInZip 的第三個參數是一個 zip_fileinfo 結構,該結構數據可全部置零,其中 dosDate 可用於填入一個時間(LastModificationTime)。它的第二個參數是 ZIP 中的文件名,若要保持目錄結構,該參數中可以保留路徑,如 foo/bar.txt。

解壓操作稍微複雜一點點。打開一個 ZIP 文件後,需要先使用 unzGetGlobalInfo64 來取得該文件的一些信息,來了解這個壓縮包裏一共包含了多少個文件,等等。目前我們用得着的就是這個文件數目。然後開始遍歷 ZIP 中的文件,初始時自動會定位在第一個文件,以後處理完一個後用 unzGoToNextFile 來跳到下一個文件。對於每個內部文件,可用 unzGetCurrentFileInfo64 來查內部文件名。這個文件名和剛纔 zipOpenNewFileInZip 的第二個參數是一樣的形式,所以有可能包含路徑。也有可能會以路徑分隔符(/)結尾,表明這是個目錄項(其實壓縮操作的時候也可以針對目錄寫入這樣的內部文件,上面沒有做)。所以接下來要根據情況創建(多級)目錄。unzGetCurrentFileInfo64 的第三個參數是 unz_file_info64 結構,其中也有一項包含了 dosDate 信息,可以還原文件時間。對於非目錄的內部文件,用 unzOpenCurrentFile,打開,然後 unzReadCurrentFile 讀取文件內容,寫入到真實文件中。unzReadCurrentFile 返回 0 表示文件讀取結束。

侷限性

  1. 只能壓縮、解壓採用 deflate 算法的 ZIP 文件。(不過此類 ZIP 應該佔了絕大多數)
  2. 由於 minizip 中相關 API 的限制,以及 ZIP 文件格式的限制,被壓縮/解壓的相關文件名必須與系統的當前代碼頁相符合。(雖然 ZIP 格式最近一次更新加入了使用 UTF8 編碼文件名的選項,但是不能保證所遇到的 ZIP 文件都是新格式的,minizip 中似乎也沒有針對此選項做什麼動作。)

轉自 http://www.cppblog.com/Streamlet/archive/2010/09/22/127368.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章