基於Zlib實現的從ZIP檔案中提取檔案資料

來源:互聯網
上載者:User

前言:呃,今天上來翻了翻之前寫的文章,其中訪問率最高的是那個『自繪RadioButton』,有好多人留言讓我發原始碼,這,也是自己懶惰,現在已經把原始碼的串連補到文章最後,也是CSDN的下載,希望對某些人有協助吧,呵呵。看著之前自己寫的東西還挺有意思的,特別是那個用JAVA做的地圖編輯器的學習版,其實是到後面寫不下去了,覺得越想越複雜。其實自己最近閑暇時分用MFC寫了個『Descent: Journey To The Dark!』的地圖編輯器,下次整理整理髮上來好了。這幾年因為工作一直很忙,說實話也沒啥多的技術積累,畢竟公司裡面混飯吃,還是要做一些雜活,雖然感覺也做得久了點。好了,言歸正傳,這次的東西是關於從ZIP壓縮包中讀取檔案的方法。其實也是那個地圖編輯器要用的一個小功能。使用了Zlib庫中contrib中的minizip的代碼,有興趣的童鞋可以去下Zlib源碼看下,我是沒這個閑情雅緻,只是實用派的。之前也在網上搜了好久,大多都是介紹如何使用Zlib庫壓縮和解壓資料,唯獨就是沒幾多人介紹如何從ZIP
Archive中抽取檔案的。不過也可能是我太急功近利了,只是想找抽取的代碼,而不想一步一步看人家的介紹。正好昨天找了點資料,自己搗鼓了下,把這抽取的代碼寫了下,可能有些人也正急著找也不一定(當然,我覺得像我這種從頭寫地圖編輯器的奇葩估計也不多了,網上大把的類庫可以用)。不過也正好,把抽取的方法寫成了個動態庫,還做了lua的封裝匯出,這樣C++和Lua就都能用了,寫得比較簡單,只是介紹個方法,其他功能還有待擴充,這裡只是個例子可以參考下實現的方案,因為代碼中好多都是測試用的,並且錯誤處理基本沒有做,以後再慢慢完善吧。代碼隨便用,本來就是個測試版的東西,出事兒了別找我就行。


平台程式是在windows平台下(XP和WIN7都試過)用VC6.0編譯的。VS的要用到的人自己轉吧,反正內部實現就這樣了,也沒用到MFC的東西。程式用到了Zlib的庫,版本是1.2.3的。還用到了lua,版本是5.1.4的,但是可以不用lua,我是順便搞的,不要的自己想辦法移除。原始碼略解  主要講下zextract工程好了,其實只是實現了3個函數,並且只有兩個還是靠譜的,其中只有一個是實用的。分別是1.『列出壓縮包內的檔案』,2.『尋找壓縮包中檔案』,3.『擷取壓縮包內的檔案』。第一個明顯是玩玩的,因為直接是printf輸出到console中,也是參考了minizip中的do_list函數的實現。第二個函數寫是寫了,但是沒啥大用,以玩玩為主。最後一個是有用的東西,實現了從壓縮包中讀取檔案的功能,其中還用到了CMemBuffer類。先看下工程的標頭檔好了。
// The following ifdef block is the standard way of creating macros which make exporting // from a DLL simpler. All files within this DLL are compiled with the ZEXTRACT_EXPORTS// symbol defined on the command line. this symbol should not be defined on any project// that uses this DLL. This way any other project whose source files include this file see // ZEXTRACT_API functions as being imported from a DLL, wheras this DLL sees symbols// defined with this macro as being exported.#ifdef ZEXTRACT_EXPORTS#define ZEXTRACT_API __declspec(dllexport)#else#define ZEXTRACT_API __declspec(dllimport)#endif#include "MemBuffer.h"#include "luna.hpp"ZEXTRACT_API void ListZip(const char* fname);ZEXTRACT_API bool FindFileInZip(const char* zfn, const char* fname);ZEXTRACT_API int GetFileInZip(CMemBuffer& buffer, const char* zfn, const char* fname, const char* password);extern "C" ZEXTRACT_API int luaopen_zextract(lua_State* L);

   講下GetFileInZip函數。一共有4個入參:

  1. CMemBuffer對象,用來儲存從壓縮包中抽取的檔案。
  2. 對應的ZIP Archive檔案路徑,相對路徑和絕對路徑皆可。
  3. 對應在ZIP Archive中要提取的檔案路徑。
  4. 密碼,如果沒有密碼則填NULL或者0即可。
  函數返回大於等於0則表示提取的檔案的大小,如果返回小於0,則是對應的錯誤碼。如果執行成功,則檔案內容存放在buffer對象中。  其餘兩個函數沒什麼介紹的必要,自己看下原始碼即可。zextract.cpp::GetFileInZip
ZEXTRACT_API int GetFileInZip(CMemBuffer& buffer, const char* zfn, const char* fname, const char* password){unzFile uf = unzOpen(zfn);if (NULL == uf){printf("unzOpen failed...\n");return -1;}int err = unzLocateFile(uf, fname, 0);if (UNZ_OK != err){printf("GetFileInZip unzLocateFile failed... error:%d\n");return err;}unz_file_info file_info;char filename_inzip[256];err = unzGetCurrentFileInfo(uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);if (UNZ_OK != err){printf("unzGetCurrentFileInfo failed... error:%d\n", err);return err;}err = unzOpenCurrentFilePassword(uf, password);if (UNZ_OK != err){printf("unzOpenCurrentFilePassword failed... error:%d\n", err);return err;}char* pBuff = new char[file_info.uncompressed_size];if (pBuff == NULL){unzCloseCurrentFile(uf);unzClose(uf);return -2;}err = unzReadCurrentFile(uf, pBuff, file_info.uncompressed_size);if (err < 0){printf("unzReadCurrentFile failed... error:%d\n", err);delete [] pBuff;unzCloseCurrentFile(uf);unzClose(uf);return err;}// Append data to the MemBufferbuffer.Append(pBuff, file_info.uncompressed_size);unzCloseCurrentFile(uf);unzClose(uf);return err;}


其中關鍵的操作函數就只有7個:

unzOpen 開啟Archive檔案
unzClose 關閉Archive檔案
unzGetCurrentFileInfo 擷取當前選擇的內部壓縮檔的資訊
unzLocateFile 定位檔案
unzOpenCurrentFilePassword 選擇開啟當前檔案
unzReadCurrentFile 讀取當前檔案
unzCloseCurrentFile 關閉當前檔案
  其中後三個是需要組合使用的,也就是說需要讀取一個內部檔案,首先要開啟,然後才是讀取,最後要關閉。而選擇當前檔案的方法有很多,這裡使用unzLocateFile函數實現的,如果找到對應檔案會把這個檔案預設選中為當前檔案。ListZip函數的實現中還有遍曆的代碼可以參考。CMemBuffer  CMemBuffer類其實只是實現了一個存放記憶體資料的功能。考慮使用一個類專門來隱藏檔資料是有多方面的考慮,比如檔案是二進位的——圖片,還有就是擷取資料是從堆上申請的記憶體,要考慮自己釋放的問題。自己手動去管理明顯會很麻煩,不如專門搞個緩衝類來管理這些記憶體資料區塊。
// MemBuffer.h: interface for the CMemBuffer class.////////////////////////////////////////////////////////////////////////#if !defined(AFX_MEMBUFFER_H__36D0136D_B57A_49FF_855B_E5545078B130__INCLUDED_)#define AFX_MEMBUFFER_H__36D0136D_B57A_49FF_855B_E5545078B130__INCLUDED_#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000class CMemBuffer  {public:CMemBuffer(int nInitLen = 16);CMemBuffer(const char* pData, int nLen = 0);CMemBuffer(const CMemBuffer& buff);virtual ~CMemBuffer();public:virtual int GetBufferLen();virtual bool Append(const char* pData, int nLen = 0);virtual char* GetBuffer();protected:bool ReAllocBuffer(int nDeltaLen);protected:char* m_pBuffer;// buffer dataint m_nCurLen;int m_nMaxLen;};#endif // !defined(AFX_MEMBUFFER_H__36D0136D_B57A_49FF_855B_E5545078B130__INCLUDED_)

  這個實現沒什麼好講的,拿到資料只要Append即可。要擷取資料直接使用GetBuffer()即可。Lua匯出部分

//////////////////////////////////////////////////////////////////////////// @Param//   zipName   which archive//   fileName  which file to extract from the archive named zipName's value//   password  password to extract file from zip// @Return//   binary data//   error numberstatic int luacf_getFileInZip(lua_State* L){CMemBuffer buff;const char* pZipName = luaL_checkstring(L, 1);const char* pFileName = luaL_checkstring(L, 2);const char* pPassword = lua_tostring(L, 3);int ret = GetFileInZip(buff, pZipName, pFileName, pPassword);if (ret >= 0){lua_pushlstring(L, buff.GetBuffer(), buff.GetBufferLen());return 1;}lua_pushnil(L);lua_pushnumber(L, ret);return 2;}const struct luaL_reg libs[] ={{"getFileInZip", luacf_getFileInZip},{NULL, NULL}};ZEXTRACT_API int luaopen_zextract(lua_State* L){luaL_register(L, "zextract", libs);return 1;}

  這部分就沒啥好說了,總之就是這麼簡單。Lua測試代碼

-- 是否是debug版本local bDGB = false;local zlib;if bDGB thenzlib = package.loadlib("zextract_d.dll", "luaopen_zextract");if type(zlib) == "function" thenzlib = zlib();endelsezlib = require "zextract";endif type(zlib) ~= "table" thenprint("loadlib error");returnendprint(zlib.getFileInZip("readme.zip", "folder/readme.txt", "123456"))

  
  這裡要講下對應的載入,前面有個debug版本的判斷,這個是根據當前編譯的版本決定的,當然也可以debug和release版本都編譯,名字是不同的,到時候自己手動修改下這個bDGB的標誌即可。二進位的檔案就不要用這個代碼了,因為最後的資料是print出來的,文本的能看下。載入動態庫的部分可以看下之前的文章《Lua指令碼調用C++動態庫》原始碼下載。目錄結構一共5個檔案夾。zextract檔案夾內為動態庫的工程目錄。zlibTest目錄為C++調用zextract產生動態庫的工程檔案。lib目錄為對應的庫檔案的目錄,其中有Zlib的庫檔案以及zextract動態庫產生的庫檔案。comm檔案為一些公用檔案存放區,比如minizip的實現源碼,還有記憶體buffer的實現源碼。bin目錄為程式的輸出目錄,內涵一個lua解析器,lua5.1.dll,zlib1.dll,還有個壓縮包的例子readme.zip。還有一個lua檔案,測試匯出庫用的。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.