需要注意Release和Debug使用的dll可能有差異,Release時需要使用對應的Dll
zlib源碼檔案中zlib123\contrib\vstudio此檔案夾包含所需的zip、unzip相關執行個體代碼minizip
最新版為zlib1.2.11
- 相關庫檔案
- 導入zlib.h,zconf.h
include "zlib.h"
include "zconf.h"
pragma comment(lib,"zlib.lib")
LNK2001:
預設調用規則不一緻,添加預定義即可
define ZLIB_WINAPI
LNK4098: defaultlib "libc.lib" conflicts with use of other libs; use /NODEFAULTLIB:library
項目->屬性中->配置屬性->連結器->輸入->在忽略特定庫中寫入打不開的檔案的名稱libc.lib
D8016:
項目”—>“屬性”—>“C/C++” “正常”—>“調試資訊格式”—>選擇“程式資料庫(/Zi)”或“無”
項目”—>“屬性”—>“C/C++” 代碼生成”—>“啟用函數集連結”—>選擇“是 (/Gy)”
LNK1104無法打開libc.lib
“項目”-->“配置屬性->連結器->輸入->忽略特定庫”後填入“LIBC.lib ”。
LNK1281 無法生成 SAFESEH 映像
“項目”-->“配置屬性->連結器-指令行 輸入 /SAFESEH:NO
1>unzip.obj : error LNK2019: 無法解析的外部符号_inflateInit2_,該符号在函數_unzOpenCurrentFile3 中被引用
1>unzip.obj : error LNK2019: 無法解析的外部符号_inflate,該符号在函數_unzReadCurrentFile 中被引用
1>unzip.obj : error LNK2019: 無法解析的外部符号_crc32,該符号在函數_unzReadCurrentFile 中被引用
1>zip.obj : error LNK2001: 無法解析的外部符号_crc32
1>unzip.obj : error LNK2019: 無法解析的外部符号_inflateEnd,該符号在函數_unzCloseCurrentFile 中被引用
1>zip.obj : error LNK2019: 無法解析的外部符号_get_crc_table,該符号在函數_zipOpenNewFileInZip4_64 中被引用
1>zip.obj : error LNK2019: 無法解析的外部符号_deflateInit2_,該符号在函數_zipOpenNewFileInZip4_64 中被引用
1>zip.obj : error LNK2019: 無法解析的外部符号_deflate,該符号在函數_zipWriteInFileInZip 中被引用
1>zip.obj : error LNK2019: 無法解析的外部符号_deflateEnd,該符号在函數_zipCloseFileInZipRaw64 中被引用
zconf.h
# define ZEXPORT WINAPI 替換為 #define ZEXPORT __cdecl
#define ZEXPORTVA WINAPIV 替換為 #define WINAPIV __cdecl
解壓縮時在inffas32.asm中報錯的處理方式&編譯時會提示inflate_fast重複定義
1. 在zlib源碼的zlibvc工程屬性-預處理器定義裡面去掉 ASMINF、ASMV 定義,屏蔽掉彙編子產品
2. 打開zlib-1.2.11\contrib\masmx86下面的彙編檔案inffas32.asm,将裡面_inflate_fast全部替換成其他任意函數名
zip相關實作
zip解壓縮
- 将zlib-1.x.x\contrib\minizip檔案夾下除miniunz.c和minizip.c以外的.h與.c檔案放到項目目錄下,并将zip.h與unzip.h導入到工程中。
- 打開需要解壓縮的檔案
//傳入zip檔案的絕對路徑,傳回一個unzFile對象。這是一個void*指針,用于指向打開的zip檔案
unzFile unzOpen(const char *path);
操作完畢後需要關閉已打開的壓縮檔案:
int unzClose OF(unzFile file);
- 擷取zip檔案的資訊
int unzGetGlobalInfo(unzFile file, unz_global_info *pglobal_info);
傳入擷取到的unzFile對象,傳入一個空的unz_global_info,函數執行完成後,會将zip檔案的資訊填充到該unz_global_info指針中。
傳入的unz_global_info定義如下:
//注意number_entry的值是zip檔案中的檔案盒檔案夾總數。與是否嵌套無關。
//例如:
//将3個檔案打包成一個zip,那麼擷取到的該值就是3;
//一個檔案夾A,含有2個檔案以及一個子檔案夾B;B中含有1個檔案。直接對A進行打包,那麼調用該函數擷取到的值是5。這是因為這個壓縮包中,共含有2個檔案//夾和3個檔案,總數為5。
typedef struct unz_global_info_s
{
uLong number_entry; // zip檔案中的檔案和檔案夾總數
uLong size_comment; // zip檔案的注釋所占大小
} unz_global_info;
傳回值
#define UNZ_OK (0)
#define UNZ_END_OF_LIST_OF_FILE (-100)
#define UNZ_ERRNO (Z_ERRNO)
#define UNZ_EOF (0)
#define UNZ_PARAMERROR (-102)
#define UNZ_BADZIPFILE (-103)
#define UNZ_INTERNALERROR (-104)
#define UNZ_CRCERROR (-105)
- 對于zip檔案中定位的目前檔案,可以通過函數來擷取其資訊:
int unzGetCurrentFileInfo( unzFile file,
unz_file_info *pfile_info,
char *szFileName,
uLong fileNameBufferSize,
void *extraField,
uLong extraFieldBufferSize,
char *szComment,
uLong commentBufferSize);
關于其參數:
① unzFile file:已打開的unzFile對象
② unz_file_info *pfile_info:一個頭檔案資訊對象的指針,傳入後,函數會将目前檔案的資訊填充到該指針指向的對象中。詳見下面說明
③ char *szFileName:用于儲存檔案名,需在外部new出足夠大的存儲空間,函數會将檔案的路徑+檔案名儲存在該對象中。注意該檔案名的路徑是個相對路徑,其最頂層為被壓縮的最上層檔案或檔案夾
④ uLong fileNameBufferSize:與③統一,告知函數new了多大的char*記憶體
⑤ void* extraField:用于儲存擴充内容,需在外部new出足夠大的存儲空間,函數會将檔案的擴充内容儲存在該對象中
⑥ uLong extraFieldBufferSize:與⑤統一,告知函數new了多大的記憶體
⑦ char* szComment:用于儲存注釋内容,需在外部new出足夠大的存儲空間,函數會将檔案的注釋内容儲存在該對象中
⑧ uLong commentBufferSize:與⑦統一,告知函數new了多大的記憶體
對于其中的unz_file_info結構體,定義如下:
typedef struct unz_file_info_s
{
uLong version; // 壓縮檔案所使用的pkware版本 2 bytes
uLong version_needed; // 解壓檔案所需pkware版本 2 bytes
uLong flag; // 全局方式位标記 2 bytes
uLong compression_method; // 壓縮方式 2 bytes
uLong dosDate; // 檔案最後修改日期時間 4 bytes
uLong crc; // CRC-32校驗 4 bytes
uLong compressed_size; // 壓縮後大小 4 bytes
uLong uncompressed_size; // 未壓縮大小 4 bytes
uLong size_filename; // 檔案名長度 2 bytes
uLong size_file_extra; // 檔案擴充字段長度 2 bytes
uLong size_file_comment; // 檔案注釋長度 2 bytes
uLong disk_num_start; // 磁盤開始号 2 bytes
uLong internal_fa; // 内部檔案屬性 2 bytes
uLong external_fa; // 外部檔案屬性 4 bytes
tm_unz tmu_date; // 檔案建立日期
} unz_file_info;
實際上,一般的zip檔案很少使用擴充與注釋。對于沒有擴充與注釋的,參數⑤⑦可以傳入null,參數⑥⑧可以傳入0。
注意擷取到的檔案名。若該檔案名以路徑分隔符”/”結尾,表明這是個檔案夾;否則表明這是個檔案。
判斷一個檔案名是檔案還是檔案夾,還可以利用external_fa字段。該字段的值會被填充為winnt.h中的FILE宏定義。通常,若external_fa = 16,即0x00000010,則說明是個檔案夾;若external_fa = 32,即0x00000020,則說明是個檔案。
在winnt.h中:
#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
#define FILE_ATTRIBUTE_ARCHIVE 0x00000020
一般來說,在這裡若external_fa !=FILE_ATTRIBUTE_DIRECTORY,則認為是個檔案。
對于已經定位到的zip中的單個檔案,就可以執行解壓縮了。解壓縮過程如下:
打開該定位到的檔案:
int unzOpenCurrentFile(unzFile file);
傳回值為執行的結果資訊。
對于加密的檔案,需要傳入密碼:
int unzOpenCurrentFilePassword(unzFile file, const char* password);
若傳回值不為UNZ_OK,則解壓出錯,可以根據傳回值判斷是檔案本身問題還是解壓問題。
讀取檔案内容:
int unzReadCurrentFile(unzFile file, voidp buf, unsigned len);
傳入unzFile對象,建立一個緩沖區用于存放讀取的内容,将緩沖區指針和大小傳入。
由于緩沖區并不一定足夠大,是以需要進行多次讀取。每次讀取,目前的讀取位置都會被修改。
例如,一個檔案讀取需要10M空間。建立了一個1M大小的緩沖區。第一次調用該函數讀取,目前位置指向檔案的開始,讀了1M大小的資料,将該資料存入緩沖區中;第二繼續調用該函數來讀取,此時目前位置由于第一次的讀取,已經指向了1M資料結尾的地方,是以第二次讀取會從1M結尾開始,讀取1M大小的資料……調用該函數10次,即可讀取完所有的資料。
檔案的讀取狀況,是依靠傳回值來判定的:
return>0:正常讀取了檔案的一部分内容,傳回值為讀取的位元組數
return==0:沒有資料被讀取,說明檔案已經讀取完畢
return<0:讀取出錯,傳回錯誤碼
将緩沖區内容寫入到建立的檔案中:
這需要借助作業系統的函數來進行。同樣是要對一個檔案進行多次寫入。
const int BUFFER_SIZE = 4096;
char szReadBuffer[BUFFER_SIZE];
while (TRUE)
{
memset(szReadBuffer, 0, BUFFER_SIZE); //清理緩沖區
int nReadFileSize = unzReadCurrentFile(unzfile, szReadBuffer, BUFFER_SIZE); //讀取
int result;
if (nReadFileSize > 0) result = 1; //讀取檔案部分資料
if (nReadFileSize == 0) result = 0; //讀取檔案結束
if (nReadFileSize < 0) result = -1; //讀取檔案失敗
switch (result)
{
case 1:
{
//讀取檔案部分資料
//将其寫入到建立的檔案中
}
break;
case 0:
{
//讀取檔案結束
unzCloseCurrentFile(unzfile);
return;
}
break;
case -1:
{
//讀取檔案失敗
unzCloseCurrentFile(unzfile);
}
break;
}
}
目前檔案解析完畢,則跳轉到下一個檔案:
int unzGoToNextFile(unzFile file);
然後重複步驟上面的幾個步驟
若成功,則return UNZ_OK
若失敗,說明已到檔案尾,則return UNZ_END_OF_LIST_OF_FILE
對zip檔案解壓縮完畢後,要将檔案關閉:
int unzClose(unzFile file);
對于zip中各個檔案和檔案夾的定位,可以使用以下函數:
int unzGoToFirstFile(unzFile file);
int unzGoToNextFile(unzFile file);
int unzLocateFile(unzFile file, const char *szFileName, int iCaseSensitivity);
關于iCaseSensitivity參數:
iCaseSensitivity = 1,則檔案名的比對區分大小寫。
iCaseSensitivity = 2,則檔案名的比對不區分大小寫。
iCaseSensitivity = 0,則檔案名的比對按作業系統自行決定。Unix使用1,Windows使用2
對于zip中各個檔案和檔案夾的位置,可以使用索引:
typedef struct unz_file_pos_s
{
uLong pos_in_zip_directory; // 檔案在zip檔案中的偏移
uLong num_of_file; // 在檔案中的索引
} unz_file_pos;
int unzGetFilePos(unzFile file, unz_file_pos* file_pos);
int unzGoToFilePos(unzFile file, unz_file_pos* file_pos);
unzGetFilePos函數會将目前定位檔案的資訊填入傳入的unz_file_pos指針中。
unzGoToFilePos函數會根據傳入的unz_file_pos指針中的資訊,将目前檔案定位到指定檔案。
擷取zip檔案的注釋:
int unzGetGlobalComment(unzFile file, char *szComment, uLong uSizeBuf);
封裝後的代碼
UZip