• 全部
...

C++ MiniZip实现目录压缩与解压

微软技术分享 微软全球最有价值专家
全栈领域优质创作者
博客专家认证
2023-12-15 09:03:08

Zlib是一个开源的数据压缩库,提供了一种通用的数据压缩和解压缩算法。它最初由Jean-Loup GaillyMark Adler开发,旨在成为一个高效、轻量级的压缩库,其被广泛应用于许多领域,包括网络通信、文件压缩、数据库系统等。其压缩算法是基于DEFLATE算法,这是一种无损数据压缩算法,通常能够提供相当高的压缩比。

在Zlib项目中的contrib目录下有一个minizip子项目,minizip实际上不是zlib库的一部分,而是一个独立的开源库,用于处理ZIP压缩文件格式。它提供了对ZIP文件的创建和解压的简单接口。minizip在很多情况下与zlib一起使用,因为ZIP压缩通常使用了DEFLATE压缩算法。通过对minizip库的二次封装则可实现针对目录的压缩与解压功能。

如果你想使用minizip通常你需要下载并编译它,然后将其链接到你的项目中。

编译Zlib库很简单,解压文件并进入到\zlib-1.3\contrib\vstudio目录下,根据自己编译器版本选择不同的目录,这里我选择vc12,进入后打开zlibvc.sln等待生成即可。

成功后可获得两个文件分别是zlibstat.libzlibwapi.lib如下图;

接着配置引用目录,这里需要多配置一个minizip头文件,该头文件是zlib里面的一个子项目。

lib库则需要包含zlibstat.libzlibwapi.lib这两个文件,此处读者可以自行放入到一个目录下;

ZIP 递归压缩目录

如下所示代码是一个使用zlib库实现的简单文件夹压缩工具的C++程序。该程序提供了压缩文件夹到 ZIP 文件的功能,支持递归地添加文件和子文件夹,利用了 Windows API 和 zlib 库的函数。

  1. #define ZLIB_WINAPI
  2. #include <string>
  3. #include <iostream>
  4. #include <vector>
  5. #include <Shlwapi.h>
  6. #include <zip.h>
  7. #include <unzip.h>
  8. #include <zlib.h>
  9. using namespace std;
  10. #pragma comment(lib, "Shlwapi.lib")
  11. #pragma comment(lib, "zlibstat.lib")
  12. bool nyAddfiletoZip(zipFile zfile, const std::string& fileNameinZip, const std::string& srcfile)
  13. {
  14. // 目录如果为空则直接返回
  15. if (NULL == zfile || fileNameinZip.empty())
  16. {
  17. return 0;
  18. }
  19. int nErr = 0;
  20. zip_fileinfo zinfo = { 0 };
  21. tm_zip tmz = { 0 };
  22. zinfo.tmz_date = tmz;
  23. zinfo.dosDate = 0;
  24. zinfo.internal_fa = 0;
  25. zinfo.external_fa = 0;
  26. char sznewfileName[MAX_PATH] = { 0 };
  27. memset(sznewfileName, 0x00, sizeof(sznewfileName));
  28. strcat_s(sznewfileName, fileNameinZip.c_str());
  29. if (srcfile.empty())
  30. {
  31. strcat_s(sznewfileName, "\\");
  32. }
  33. nErr = zipOpenNewFileInZip(zfile, sznewfileName, &zinfo, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION);
  34. if (nErr != ZIP_OK)
  35. {
  36. return false;
  37. }
  38. if (!srcfile.empty())
  39. {
  40. // 打开源文件
  41. FILE* srcfp = _fsopen(srcfile.c_str(), "rb", _SH_DENYNO);
  42. if (NULL == srcfp)
  43. {
  44. std::cout << "打开源文件失败" << std::endl;
  45. return false;
  46. }
  47. // 读入源文件写入zip文件
  48. int numBytes = 0;
  49. char* pBuf = new char[1024 * 100];
  50. if (NULL == pBuf)
  51. {
  52. std::cout << "新建缓冲区失败" << std::endl;
  53. return 0;
  54. }
  55. while (!feof(srcfp))
  56. {
  57. memset(pBuf, 0x00, sizeof(pBuf));
  58. numBytes = fread(pBuf, 1, sizeof(pBuf), srcfp);
  59. nErr = zipWriteInFileInZip(zfile, pBuf, numBytes);
  60. if (ferror(srcfp))
  61. {
  62. break;
  63. }
  64. }
  65. delete[] pBuf;
  66. fclose(srcfp);
  67. }
  68. zipCloseFileInZip(zfile);
  69. return true;
  70. }
  71. bool nyCollectfileInDirtoZip(zipFile zfile, const std::string& filepath, const std::string& parentdirName)
  72. {
  73. if (NULL == zfile || filepath.empty())
  74. {
  75. return false;
  76. }
  77. bool bFile = false;
  78. std::string relativepath = "";
  79. WIN32_FIND_DATAA findFileData;
  80. char szpath[MAX_PATH] = { 0 };
  81. if (::PathIsDirectoryA(filepath.c_str()))
  82. {
  83. strcpy_s(szpath, sizeof(szpath) / sizeof(szpath[0]), filepath.c_str());
  84. int len = strlen(szpath) + strlen("\\*.*") + 1;
  85. strcat_s(szpath, len, "\\*.*");
  86. }
  87. else
  88. {
  89. bFile = true;
  90. strcpy_s(szpath, sizeof(szpath) / sizeof(szpath[0]), filepath.c_str());
  91. }
  92. HANDLE hFile = ::FindFirstFileA(szpath, &findFileData);
  93. if (NULL == hFile)
  94. {
  95. return false;
  96. }
  97. do
  98. {
  99. if (parentdirName.empty())
  100. relativepath = findFileData.cFileName;
  101. else
  102. // 生成zip文件中的相对路径
  103. relativepath = parentdirName + "\\" + findFileData.cFileName;
  104. // 如果是目录
  105. if (findFileData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
  106. {
  107. // 去掉目录中的.当前目录和..前一个目录
  108. if (strcmp(findFileData.cFileName, ".") != 0 && strcmp(findFileData.cFileName, "..") != 0)
  109. {
  110. nyAddfiletoZip(zfile, relativepath, "");
  111. char szTemp[MAX_PATH] = { 0 };
  112. strcpy_s(szTemp, filepath.c_str());
  113. strcat_s(szTemp, "\\");
  114. strcat_s(szTemp, findFileData.cFileName);
  115. nyCollectfileInDirtoZip(zfile, szTemp, relativepath);
  116. }
  117. continue;
  118. }
  119. char szTemp[MAX_PATH] = { 0 };
  120. if (bFile)
  121. {
  122. //注意:处理单独文件的压缩
  123. strcpy_s(szTemp, filepath.c_str());
  124. }
  125. else
  126. {
  127. //注意:处理目录文件的压缩
  128. strcpy_s(szTemp, filepath.c_str());
  129. strcat_s(szTemp, "\\");
  130. strcat_s(szTemp, findFileData.cFileName);
  131. }
  132. nyAddfiletoZip(zfile, relativepath, szTemp);
  133. } while (::FindNextFileA(hFile, &findFileData));
  134. FindClose(hFile);
  135. return true;
  136. }
  137. /*
  138. * 函数功能 : 压缩文件夹到目录
  139. * 备 注 : dirpathName 源文件/文件夹
  140. * zipFileName 目的压缩包
  141. * parentdirName 压缩包内名字(文件夹名)
  142. */
  143. bool nyCreateZipfromDir(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName)
  144. {
  145. bool bRet = false;
  146. /*
  147. APPEND_STATUS_CREATE 创建追加
  148. APPEND_STATUS_CREATEAFTER 创建后追加(覆盖方式)
  149. APPEND_STATUS_ADDINZIP 直接追加
  150. */
  151. zipFile zFile = NULL;
  152. if (!::PathFileExistsA(zipfileName.c_str()))
  153. {
  154. zFile = zipOpen(zipfileName.c_str(), APPEND_STATUS_CREATE);
  155. }
  156. else
  157. {
  158. zFile = zipOpen(zipfileName.c_str(), APPEND_STATUS_ADDINZIP);
  159. }
  160. if (NULL == zFile)
  161. {
  162. std::cout << "创建ZIP文件失败" << std::endl;
  163. return bRet;
  164. }
  165. if (nyCollectfileInDirtoZip(zFile, dirpathName, parentdirName))
  166. {
  167. bRet = true;
  168. }
  169. zipClose(zFile, NULL);
  170. return bRet;
  171. }

主要功能

nyCreateZipfromDir函数

bool nyCreateZipfromDir(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName);

功能:压缩文件夹到指定的 ZIP 文件。

参数:

  • dirpathName:源文件夹路径。
  • zipfileName:目标 ZIP 文件路径。
  • parentdirName:在 ZIP 文件内的文件夹名(如果为空则不指定目录)。

nyCollectfileInDirtoZip 函数

bool nyCollectfileInDirtoZip(zipFile zfile, const std::string& filepath, const std::string& parentdirName);

功能:递归地收集文件夹中的文件,并将它们添加到已打开的 ZIP 文件中。

参数:

  • zfile:已打开的 ZIP 文件。
  • filepath:文件夹路径。
  • parentdirName:在 ZIP 文件内的相对文件夹名。

nyAddfiletoZip 函数

bool nyAddfiletoZip(zipFile zfile, const std::string& fileNameinZip, const std::string& srcfile);

功能:将指定文件添加到已打开的 ZIP 文件中。

参数:

  • zfile:已打开的 ZIP 文件。
  • fileNameinZip:在 ZIP 文件内的相对文件路径。
  • srcfile:源文件路径。

程序流程

  • 文件夹压缩参数设置: 用户提供源文件夹路径、目标 ZIP 文件路径,以及在 ZIP 文件内的文件夹名。
  • ZIP 文件打开: 根据目标 ZIP 文件是否存在,使用 zipOpen 函数打开 ZIP 文件。
  • 文件夹递归添加: 使用 nyCollectfileInDirtoZip 函数递归地收集文件夹中的文件,并通过 nyAddfiletoZip 函数将它们添加到 ZIP 文件中。
  • ZIP 文件关闭: 使用 zipClose 函数关闭 ZIP 文件。

示例用法

  1. int main(int argc, char* argv[])
  2. {
  3. std::string dirpath = "D:\\lyshark\\test"; // 源文件/文件夹
  4. std::string zipfileName = "D:\\lyshark\\test.zip"; // 目的压缩包
  5. bool ref = nyCreateZipfromDir(dirpath, zipfileName, "lyshark"); // 包内文件名<如果为空则压缩时不指定目录>
  6. std::cout << "[LyShark] 压缩状态 " << ref << std::endl;
  7. system("pause");
  8. return 0;
  9. }

上述调用代码,参数1指定为需要压缩的文件目录,参数2指定为需要压缩成目录名,参数3为压缩后该目录的名字。

ZIP 递归解压目录

在这个C++程序中,实现了递归解压缩ZIP文件的功能。程序提供了以下主要功能:

  • replace_all 函数: 用于替换字符串中的指定子串。
  • CreatedMultipleDirectory 函数: 用于创建多级目录,确保解压缩时的目录结构存在。
  • UnzipFile 函数: 用于递归解压缩 ZIP 文件。该函数打开 ZIP 文件,获取文件信息,然后逐个解析和处理 ZIP 文件中的文件或目录。
  1. #define ZLIB_WINAPI
  2. #include <string>
  3. #include <iostream>
  4. #include <vector>
  5. #include <Shlwapi.h>
  6. #include <zip.h>
  7. #include <unzip.h>
  8. #include <zlib.h>
  9. using namespace std;
  10. #pragma comment(lib, "Shlwapi.lib")
  11. #pragma comment(lib, "zlibstat.lib")
  12. // 将字符串内的old_value替换成new_value
  13. std::string& replace_all(std::string& str, const std::string& old_value, const std::string& new_value)
  14. {
  15. while (true)
  16. {
  17. std::string::size_type pos(0);
  18. if ((pos = str.find(old_value)) != std::string::npos)
  19. str.replace(pos, old_value.length(), new_value);
  20. else
  21. break;
  22. }
  23. return str;
  24. }
  25. // 创建多级目录
  26. BOOL CreatedMultipleDirectory(const std::string& direct)
  27. {
  28. std::string Directoryname = direct;
  29. if (Directoryname[Directoryname.length() - 1] != '\\')
  30. {
  31. Directoryname.append(1, '\\');
  32. }
  33. std::vector< std::string> vpath;
  34. std::string strtemp;
  35. BOOL bSuccess = FALSE;
  36. for (int i = 0; i < Directoryname.length(); i++)
  37. {
  38. if (Directoryname[i] != '\\')
  39. {
  40. strtemp.append(1, Directoryname[i]);
  41. }
  42. else
  43. {
  44. vpath.push_back(strtemp);
  45. strtemp.append(1, '\\');
  46. }
  47. }
  48. std::vector< std::string>::iterator vIter = vpath.begin();
  49. for (; vIter != vpath.end(); vIter++)
  50. {
  51. bSuccess = CreateDirectoryA(vIter->c_str(), NULL) ? TRUE : FALSE;
  52. }
  53. return bSuccess;
  54. }
  55. /*
  56. * 函数功能 : 递归解压文件目录
  57. * 备 注 : strFilePath 压缩包路径
  58. * strTempPath 解压到
  59. */
  60. void UnzipFile(const std::string& strFilePath, const std::string& strTempPath)
  61. {
  62. int nReturnValue;
  63. string tempFilePath;
  64. string srcFilePath(strFilePath);
  65. string destFilePath;
  66. // 打开zip文件
  67. unzFile unzfile = unzOpen(srcFilePath.c_str());
  68. if (unzfile == NULL)
  69. {
  70. return;
  71. }
  72. // 获取zip文件的信息
  73. unz_global_info* pGlobalInfo = new unz_global_info;
  74. nReturnValue = unzGetGlobalInfo(unzfile, pGlobalInfo);
  75. if (nReturnValue != UNZ_OK)
  76. {
  77. std::cout << "数据包: " << pGlobalInfo->number_entry << endl;
  78. return;
  79. }
  80. // 解析zip文件
  81. unz_file_info* pFileInfo = new unz_file_info;
  82. char szZipFName[MAX_PATH] = { 0 };
  83. char szExtraName[MAX_PATH] = { 0 };
  84. char szCommName[MAX_PATH] = { 0 };
  85. // 存放从zip中解析出来的内部文件名
  86. for (int i = 0; i < pGlobalInfo->number_entry; i++)
  87. {
  88. // 解析得到zip中的文件信息
  89. nReturnValue = unzGetCurrentFileInfo(unzfile, pFileInfo, szZipFName, MAX_PATH, szExtraName, MAX_PATH, szCommName, MAX_PATH);
  90. if (nReturnValue != UNZ_OK)
  91. return;
  92. std::cout << "解压文件名: " << szZipFName << endl;
  93. string strZipFName = szZipFName;
  94. // 如果是目录则执行创建递归目录名
  95. if (pFileInfo->external_fa == FILE_ATTRIBUTE_DIRECTORY || (strZipFName.rfind('/') == strZipFName.length() - 1))
  96. {
  97. destFilePath = strTempPath + "//" + szZipFName;
  98. CreateDirectoryA(destFilePath.c_str(), NULL);
  99. }
  100. // 如果是文件则解压缩并创建
  101. else
  102. {
  103. // 创建文件 保存完整路径
  104. string strFullFilePath;
  105. tempFilePath = strTempPath + "/" + szZipFName;
  106. strFullFilePath = tempFilePath;
  107. int nPos = tempFilePath.rfind("/");
  108. int nPosRev = tempFilePath.rfind("\\");
  109. if (nPosRev == string::npos && nPos == string::npos)
  110. continue;
  111. size_t nSplitPos = nPos > nPosRev ? nPos : nPosRev;
  112. destFilePath = tempFilePath.substr(0, nSplitPos + 1);
  113. if (!PathIsDirectoryA(destFilePath.c_str()))
  114. {
  115. // 将路径格式统一
  116. destFilePath = replace_all(destFilePath, "/", "\\");
  117. // 创建多级目录
  118. int bRet = CreatedMultipleDirectory(destFilePath);
  119. }
  120. strFullFilePath = replace_all(strFullFilePath, "/", "\\");
  121. HANDLE hFile = CreateFileA(strFullFilePath.c_str(), GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL);
  122. if (hFile == INVALID_HANDLE_VALUE)
  123. {
  124. return;
  125. }
  126. // 打开文件
  127. nReturnValue = unzOpenCurrentFile(unzfile);
  128. if (nReturnValue != UNZ_OK)
  129. {
  130. CloseHandle(hFile);
  131. return;
  132. }
  133. // 读取文件
  134. uLong BUFFER_SIZE = pFileInfo->uncompressed_size;;
  135. void* szReadBuffer = NULL;
  136. szReadBuffer = (char*)malloc(BUFFER_SIZE);
  137. if (NULL == szReadBuffer)
  138. {
  139. break;
  140. }
  141. while (TRUE)
  142. {
  143. memset(szReadBuffer, 0, BUFFER_SIZE);
  144. int nReadFileSize = 0;
  145. nReadFileSize = unzReadCurrentFile(unzfile, szReadBuffer, BUFFER_SIZE);
  146. // 读取文件失败
  147. if (nReadFileSize < 0)
  148. {
  149. unzCloseCurrentFile(unzfile);
  150. CloseHandle(hFile);
  151. return;
  152. }
  153. // 读取文件完毕
  154. else if (nReadFileSize == 0)
  155. {
  156. unzCloseCurrentFile(unzfile);
  157. CloseHandle(hFile);
  158. break;
  159. }
  160. // 写入读取的内容
  161. else
  162. {
  163. DWORD dWrite = 0;
  164. BOOL bWriteSuccessed = WriteFile(hFile, szReadBuffer, BUFFER_SIZE, &dWrite, NULL);
  165. if (!bWriteSuccessed)
  166. {
  167. unzCloseCurrentFile(unzfile);
  168. CloseHandle(hFile);
  169. return;
  170. }
  171. }
  172. }
  173. free(szReadBuffer);
  174. }
  175. unzGoToNextFile(unzfile);
  176. }
  177. delete pFileInfo;
  178. delete pGlobalInfo;
  179. // 关闭
  180. if (unzfile)
  181. {
  182. unzClose(unzfile);
  183. }
  184. }

主要功能

replace_all 函数

  1. std::string& replace_all(std::string& str, const std::string& old_value, const std::string& new_value)

功能:在字符串 str 中替换所有的 old_value 为 new_value。

参数:

  • str:待处理的字符串。
  • old_value:要被替换的子串。
  • new_value:替换后的新子串。

返回值:替换后的字符串。

CreatedMultipleDirectory 函数

  1. BOOL CreatedMultipleDirectory(const std::string& direct)

功能:创建多级目录,确保路径存在。

参数:

  • direct:目录路径。
  • 返回值:如果成功创建目录返回 TRUE,否则返回 FALSE。

UnzipFile 函数

  1. void UnzipFile(const std::string& strFilePath, const std::string& strTempPath)

功能:递归解压缩 ZIP 文件。

参数:

  • strFilePath:ZIP 文件路径。
  • strTempPath:解压到的目标路径。

该函数打开 ZIP 文件,获取文件信息,然后逐个解析和处理 ZIP 文件中的文件或目录。在解析过程中,根据文件或目录的属性,创建相应的目录结构,然后将文件写入目标路径。

示例用法

  1. int main(int argc, char* argv[])
  2. {
  3. std::string srcFilePath = "D:\\lyshark\\test.zip";
  4. std::string tempdir = "D:\\lyshark\\test";
  5. // 如果传入目录不存在则创建
  6. if (!::PathFileExistsA(tempdir.c_str()))
  7. {
  8. CreatedMultipleDirectory(tempdir);
  9. }
  10. // 调用解压函数
  11. UnzipFile(srcFilePath, tempdir);
  12. system("pause");
  13. return 0;
  14. }

案例中,首先在解压缩之前判断传入目录是否存在,如果不存在则需要调用API创建目录,如果存在则直接调用UnzipFIle解压缩函数,实现解包,输出效果图如下;

...全文
给本帖投票
162 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

5,086

社区成员

发帖
与我相关
我的任务
社区描述
微软技术社区为中国的开发者们提供一个技术干货传播平台,传递微软全球的技术和产品最新动态,分享各大技术方向的学习资源,同时也涵盖针对不同行业和场景的实践案例,希望可以全方位地帮助你获取更多知识和技能。
windowsmicrosoft 企业社区
社区管理员
  • 山月照空舟
  • 郑子铭
加入社区
社区公告

微软技术社区为中国的开发者们提供一个技术干货传播平台,传递微软全球的技术和产品最新动态,分享各大技术方向的学习资源,同时也涵盖针对不同行业和场景的实践案例,希望可以全方位地帮助你获取更多知识和技能。

予力众生,成就不凡!微软致力于用技术改变世界,助力企业实现数字化转型。

试试用AI创作助手写篇文章吧

手机看
关注公众号

关注公众号

客服 返回
顶部