【VC++技术杂谈008】使用zlib解压zip压缩文件
最近因为项目的需要,要对zip压缩文件进行批量解压。在网上查阅了相关的资料后,最终使用zlib开源库实现了该功能。本文将对zlib开源库进行简单介绍,并给出一个使用zlib开源库对zip压缩文件进行解压的示例程序。
1.zlib开源库
zlib是应用最广泛的压缩与解压缩zip文件的免费开源库,提供了数据压缩与解压缩的函式库。
zlib中最关键的函数有以下两个:
(1)int compress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);
(2)int uncompress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);
其中,函数compress()用于将源缓冲区数据压缩到目的缓冲区,函数uncompress()用于将源缓冲区数据解压到目的缓冲区。
由此可见,zlib只是一个针对gzip以及deflate算法的库,用于将一段内存压缩/解压之后放到另一段内存上,这离压缩/解压文件甚至文件夹的目标还很远。但是,它提供了一个叫做minizip的例子给出了操作zip文件的方法。
2.minizip简介
minizip是zlib的上层库,它封装了与zip文件相关的操作。
minizip中与解压缩相关的API有以下几个:
(1)unzFile unzOpen(const char *path);
(2)int unzClose(unzFile file);
(3)int unzGetGlobalInfo(unzFile file, unz_global_info *pglobal_info);
(4)int unzGoToNextFile(unzFile file);
(5)int unzGetCurrentFileInfo(unzFile file, unz_file_info *pfile_info, char *szFileName, uLong fileNameBufferSize,
void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize);
(6)int unzOpenCurrentFile(unzFile file);
(7)int unzCloseCurrentFile(unzFile file);
(8)int unzReadCurrentFile(unzFile file, voidp buf, unsigned len);
有了以上这些API,我们就可以对zip文件进行解压缩了。一个完整的解压过程应该包含以下这些步骤:
(1)调用unzOpen()函数打开一个zip压缩文件,其参数是zip压缩文件的路径。
(2)调用unzGetGlobalInfo()函数来获取zip压缩文件的一些信息(如内部文件个数等),这些信息会保存在传入参数pglobal_info中。
(3)然后开始遍历zip文件中的内部文件,初始时会自动定位到第一个内部文件,处理完一个内部文件后可以使用unzGoToNextFile()函数来跳转到下一个内部文件。
(4)对于每个内部文件来说,可以先调用unzGetCurrentFileInfo()函数来获取该内部文件信息(如文件的路径、文件大小等),这些信息会保存在传入参数pfile_info中。
(5)调用unzOpenCurrentFile()函数打开该内部文件。
(6)调用unzReadCurrentFile()函数读取该内部文件内容。
(7)该内部文件读取完毕之后,调用unzCloseCurrentFile()函数对内部文件进行关闭。
(8)zip文件中的所有内部文件遍历完成之后,调用unzClose()函数关闭打开的zip压缩文件。
3.示例程序
了解了以上的内容之后,我们就可以编写程序使用zlib以及minizip对zip压缩文件进行解压缩了。
3.1加载相关的头文件及库文件
在使用zlib以及minizip之前,我们需要加载相关的头文件及库文件到工程中。需要加载的头文件有zlib.h、unzip.h、zip.h。需要加载的库文件有zlib.lib、minizip.lib。需要添加的动态链接库zlib1.dll。这些文件都可以从网上下载得到。
1 #include "zlib/zlib.h" 2 #include "zlib/unzip.h" 3 #include "zlib/zip.h" 4 #pragma comment(lib, "zlib.lib") 5 #pragma comment(lib, "minizip.lib")
3.2配置工程
因为zlib以及minizip是用C语言编写的,在VC6.0中使用时,需要对工程进行如下配置,否则会出现编译链接通不过的问题。
(1)在“工程”、“设置”中选择“连接”标签页,在“分类”中选择输入,在“忽略库”中加入MSVCRT。
(2)在“工程”、“设置”中选择“C/C++”标签页,在“分类”中选择Code Generation,在“Use run-time library”中选择“Debug Multithreaded DLL”。
(3)在“工程”、“设置”中选择“C/C++”标签页,在“分类”中选择常规,在“预处理程序定义”中加入_AFXDLL。
3.3示例程序
如下的示例程序演示了如何调用minizip中的API对zip文件进行解压。
1 /* 2 * 函数功能 : 解压zip文件 3 * 备 注 : 参数strFilePath表示zip压缩文件的路径 4 * 参数strTempPath表示要解压到的文件目录 5 * 作 者 : 博客园 依旧淡然(http://www.cnblogs.com/menlsh/) 6 */ 7 void CZlibDemoDlg::UnzipFile(CString strFilePath, CString strTempPath) 8 { 9 int nReturnValue; 10 11 //打开zip文件 12 unzFile unzfile = unzOpen(strFilePath); 13 if(unzfile == NULL) 14 { 15 MessageBox("打开zip文件失败!", "提示", MB_OK|MB_ICONWARNING); 16 return; 17 } 18 19 //获取zip文件的信息 20 unz_global_info* pGlobalInfo = new unz_global_info; 21 nReturnValue = unzGetGlobalInfo(unzfile, pGlobalInfo); 22 if(nReturnValue != UNZ_OK) 23 { 24 MessageBox("获取zip文件信息失败!", "提示", MB_OK|MB_ICONWARNING); 25 return; 26 } 27 28 //解析zip文件 29 unz_file_info* pFileInfo = new unz_file_info; 30 char szZipFName[MAX_PATH]; //存放从zip中解析出来的内部文件名 31 for(int i=0; i<pGlobalInfo->number_entry; i++) 32 { 33 //解析得到zip中的文件信息 34 nReturnValue = unzGetCurrentFileInfo(unzfile, pFileInfo, szZipFName, MAX_PATH, 35 NULL, 0, NULL, 0); 36 if(nReturnValue != UNZ_OK) 37 { 38 MessageBox("解析zip文件信息失败!", "提示", MB_OK|MB_ICONWARNING); 39 return; 40 } 41 42 //判断是文件夹还是文件 43 switch(pFileInfo->external_fa) 44 { 45 case FILE_ATTRIBUTE_DIRECTORY: //文件夹 46 { 47 CString strDiskPath = strTempPath + _T("//") + szZipFName; 48 CreateDirectory(strDiskPath, NULL); 49 } 50 break; 51 default: //文件 52 { 53 //创建文件 54 CString strDiskFile = strTempPath + _T("//") + szZipFName; 55 HANDLE hFile = CreateFile(strDiskFile, GENERIC_WRITE, 56 0, NULL, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL); 57 if(hFile == INVALID_HANDLE_VALUE) 58 { 59 MessageBox("创建文件失败!", "提示", MB_OK|MB_ICONWARNING); 60 return; 61 } 62 63 //打开文件 64 nReturnValue = unzOpenCurrentFile(unzfile); 65 if(nReturnValue != UNZ_OK) 66 { 67 MessageBox("打开文件失败!", "提示", MB_OK|MB_ICONWARNING); 68 CloseHandle(hFile); 69 return; 70 } 71 72 //读取文件 73 const int BUFFER_SIZE = 4096; 74 char szReadBuffer[BUFFER_SIZE]; 75 while(TRUE) 76 { 77 memset(szReadBuffer, 0, BUFFER_SIZE); 78 int nReadFileSize = unzReadCurrentFile(unzfile, szReadBuffer, BUFFER_SIZE); 79 if(nReadFileSize < 0) //读取文件失败 80 { 81 MessageBox("读取文件失败!", "提示", MB_OK|MB_ICONWARNING); 82 unzCloseCurrentFile(unzfile); 83 CloseHandle(hFile); 84 return; 85 } 86 else if(nReadFileSize == 0) //读取文件完毕 87 { 88 unzCloseCurrentFile(unzfile); 89 CloseHandle(hFile); 90 break; 91 } 92 else //写入读取的内容 93 { 94 DWORD dWrite = 0; 95 BOOL bWriteSuccessed = WriteFile(hFile, szReadBuffer, BUFFER_SIZE, &dWrite, NULL); 96 if(!bWriteSuccessed) 97 { 98 MessageBox("读取文件失败!", "提示", MB_OK|MB_ICONWARNING); 99 unzCloseCurrentFile(unzfile); 100 CloseHandle(hFile); 101 return; 102 } 103 } 104 } 105 } 106 break; 107 } 108 unzGoToNextFile(unzfile); 109 } 110 111 //关闭 112 if(unzfile) 113 { 114 unzClose(unzfile); 115 } 116 }
3.4运行结果
调用上述的UnzipFile()方法对某个zip文件进行解压,如图1所示。
图1 解压zip文件
解压后,可以看到文件夹123中的内容如图2所示。
图2 解压后的文件