C/C++ Zlib库封装MyZip压缩类

守听  金牌会员 | 2024-1-17 19:07:59 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 893|帖子 893|积分 2679

Zlib是一个开源的数据压缩库,提供了一种通用的数据压缩和解压缩算法。它最初由Jean-Loup Gailly和Mark Adler开发,旨在成为一个高效、轻量级的压缩库,其被广泛应用于许多领域,包括网络通信、文件压缩、数据库系统等。其压缩算法是基于DEFLATE算法,这是一种无损数据压缩算法,通常能够提供相当高的压缩比。
在软件开发中,文件的压缩和解压缩是一项常见的任务,而ZIP是一种被广泛应用的压缩格式。为了方便地处理ZIP压缩和解压缩操作,开发者通常使用各种编程语言和库来实现这些功能。本文将聚焦于一个简化的C++实现,通过分析代码,我们将深入了解其设计和实现细节。
类的功能实现

MyZip类旨在提供简单易用的ZIP压缩和解压缩功能。通过成员函数Compress和UnCompress,该类使得对目录的ZIP压缩和ZIP文件的解压变得相对容易。
ZIP压缩函数 Compress
Compress函数通过zlib库提供的ZIP压缩功能,递归地将目录下的文件添加到ZIP文件中。其中,nyCollectfileInDirtoZip函数负责遍历目录,而nyAddfiletoZip函数则用于添加文件到ZIP中。这种设计使得代码模块化,易于理解。
ZIP解压函数 UnCompress
UnCompress函数通过zlib库提供的ZIP解压功能,将ZIP文件解压到指定目录。函数中使用了unz系列函数来遍历ZIP文件中的文件信息,并根据文件类型进行相应的处理。这包括创建目录和写入文件,使得解压后的目录结构与ZIP文件一致。
将如上的压缩与解压方法封装成MyZip类,调用zip.Compress()实现压缩目录,调用zip.UnCompress()则实现解压缩目录。这些函数使用了zlib库的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. class MyZip
  13. {
  14. private:
  15.   // 向ZIP文件中添加文件
  16.   bool nyAddfiletoZip(zipFile zfile, const std::string& fileNameinZip, const std::string& srcfile)
  17.   {
  18.     if (NULL == zfile || fileNameinZip.empty())
  19.     {
  20.       return false;
  21.     }
  22.     int nErr = 0;
  23.     zip_fileinfo zinfo = { 0 };
  24.     tm_zip tmz = { 0 };
  25.     zinfo.tmz_date = tmz;
  26.     zinfo.dosDate = 0;
  27.     zinfo.internal_fa = 0;
  28.     zinfo.external_fa = 0;
  29.     // 构建新文件名
  30.     char sznewfileName[MAX_PATH] = { 0 };
  31.     memset(sznewfileName, 0x00, sizeof(sznewfileName));
  32.     strcat_s(sznewfileName, fileNameinZip.c_str());
  33.     if (srcfile.empty())
  34.     {
  35.       strcat_s(sznewfileName, "\");
  36.     }
  37.     // 在ZIP中打开新文件
  38.     nErr = zipOpenNewFileInZip(zfile, sznewfileName, &zinfo, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION);
  39.     if (nErr != ZIP_OK)
  40.     {
  41.       return false;
  42.     }
  43.     // 如果有源文件,读取并写入ZIP文件
  44.     if (!srcfile.empty())
  45.     {
  46.       FILE* srcfp = _fsopen(srcfile.c_str(), "rb", _SH_DENYNO);
  47.       if (NULL == srcfp)
  48.       {
  49.         return false;
  50.       }
  51.       int numBytes = 0;
  52.       char* pBuf = new char[1024 * 100];
  53.       if (NULL == pBuf)
  54.       {
  55.         return false;
  56.       }
  57.       // 逐块读取源文件并写入ZIP
  58.       while (!feof(srcfp))
  59.       {
  60.         memset(pBuf, 0x00, sizeof(pBuf));
  61.         numBytes = fread(pBuf, 1, sizeof(pBuf), srcfp);
  62.         nErr = zipWriteInFileInZip(zfile, pBuf, numBytes);
  63.         if (ferror(srcfp))
  64.         {
  65.           break;
  66.         }
  67.       }
  68.       delete[] pBuf;
  69.       fclose(srcfp);
  70.     }
  71.     // 关闭ZIP文件中的当前文件
  72.     zipCloseFileInZip(zfile);
  73.     return true;
  74.   }
  75.   // 递归地将目录下的文件添加到ZIP
  76.   bool nyCollectfileInDirtoZip(zipFile zfile, const std::string& filepath, const std::string& parentdirName)
  77.   {
  78.     if (NULL == zfile || filepath.empty())
  79.     {
  80.       return false;
  81.     }
  82.     bool bFile = false;
  83.     std::string relativepath = "";
  84.     WIN32_FIND_DATAA findFileData;
  85.     char szpath[MAX_PATH] = { 0 };
  86.     if (::PathIsDirectoryA(filepath.c_str()))
  87.     {
  88.       strcpy_s(szpath, sizeof(szpath) / sizeof(szpath[0]), filepath.c_str());
  89.       int len = strlen(szpath) + strlen("\\*.*") + 1;
  90.       strcat_s(szpath, len, "\\*.*");
  91.     }
  92.     else
  93.     {
  94.       bFile = true;
  95.       strcpy_s(szpath, sizeof(szpath) / sizeof(szpath[0]), filepath.c_str());
  96.     }
  97.     HANDLE hFile = ::FindFirstFileA(szpath, &findFileData);
  98.     if (NULL == hFile)
  99.     {
  100.       return false;
  101.     }
  102.     do
  103.     {
  104.       // 构建相对路径
  105.       if (parentdirName.empty())
  106.         relativepath = findFileData.cFileName;
  107.       else
  108.         relativepath = parentdirName + "\" + findFileData.cFileName;
  109.       // 如果是目录,递归处理子目录
  110.       if (findFileData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
  111.       {
  112.         if (strcmp(findFileData.cFileName, ".") != 0 && strcmp(findFileData.cFileName, "..") != 0)
  113.         {
  114.           nyAddfiletoZip(zfile, relativepath, "");
  115.           char szTemp[MAX_PATH] = { 0 };
  116.           strcpy_s(szTemp, filepath.c_str());
  117.           strcat_s(szTemp, "\");
  118.           strcat_s(szTemp, findFileData.cFileName);
  119.           nyCollectfileInDirtoZip(zfile, szTemp, relativepath);
  120.         }
  121.         continue;
  122.       }
  123.       char szTemp[MAX_PATH] = { 0 };
  124.       if (bFile)
  125.       {
  126.         strcpy_s(szTemp, filepath.c_str());
  127.       }
  128.       else
  129.       {
  130.         strcpy_s(szTemp, filepath.c_str());
  131.         strcat_s(szTemp, "\");
  132.         strcat_s(szTemp, findFileData.cFileName);
  133.       }
  134.       // 将文件添加到ZIP
  135.       nyAddfiletoZip(zfile, relativepath, szTemp);
  136.     } while (::FindNextFileA(hFile, &findFileData));
  137.     FindClose(hFile);
  138.     return true;
  139.   }
  140.   // 替换字符串中的所有指定子串
  141.   std::string& replace_all(std::string& str, const std::string& old_value, const std::string& new_value)
  142.   {
  143.     while (true)
  144.     {
  145.       std::string::size_type pos(0);
  146.       if ((pos = str.find(old_value)) != std::string::npos)
  147.         str.replace(pos, old_value.length(), new_value);
  148.       else
  149.         break;
  150.     }
  151.     return str;
  152.   }
  153.   // 创建多级目录
  154.   BOOL CreatedMultipleDirectory(const std::string& direct)
  155.   {
  156.     std::string Directoryname = direct;
  157.     if (Directoryname[Directoryname.length() - 1] != '\\')
  158.     {
  159.       Directoryname.append(1, '\\');
  160.     }
  161.     std::vector< std::string> vpath;
  162.     std::string strtemp;
  163.     BOOL  bSuccess = FALSE;
  164.     // 遍历目录字符串,逐级创建目录
  165.     for (int i = 0; i < Directoryname.length(); i++)
  166.     {
  167.       if (Directoryname[i] != '\\')
  168.       {
  169.         strtemp.append(1, Directoryname[i]);
  170.       }
  171.       else
  172.       {
  173.         vpath.push_back(strtemp);
  174.         strtemp.append(1, '\\');
  175.       }
  176.     }
  177.     std::vector< std::string>::iterator vIter = vpath.begin();
  178.     for (; vIter != vpath.end(); vIter++)
  179.     {
  180.       bSuccess = CreateDirectoryA(vIter->c_str(), NULL) ? TRUE : FALSE;
  181.     }
  182.     return bSuccess;
  183.   }
  184. public:
  185.   // 压缩目录
  186.   bool Compress(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName)
  187.   {
  188.     bool bRet = false;
  189.     zipFile zFile = NULL;
  190.     // 根据ZIP文件是否存在选择打开方式
  191.     if (!::PathFileExistsA(zipfileName.c_str()))
  192.     {
  193.       zFile = zipOpen(zipfileName.c_str(), APPEND_STATUS_CREATE);
  194.     }
  195.     else
  196.     {
  197.       zFile = zipOpen(zipfileName.c_str(), APPEND_STATUS_ADDINZIP);
  198.     }
  199.     if (NULL == zFile)
  200.     {
  201.       return bRet;
  202.     }
  203.     // 将目录下的文件添加到ZIP
  204.     if (nyCollectfileInDirtoZip(zFile, dirpathName, parentdirName))
  205.     {
  206.       bRet = true;
  207.     }
  208.     zipClose(zFile, NULL);
  209.     return bRet;
  210.   }
  211.   // 解压目录
  212.   bool UnCompress(const std::string& strFilePath, const std::string& strTempPath)
  213.   {
  214.     int nReturnValue;
  215.     string tempFilePath;
  216.     string srcFilePath(strFilePath);
  217.     string destFilePath;
  218.     // 打开ZIP文件
  219.     unzFile unzfile = unzOpen(srcFilePath.c_str());
  220.     if (unzfile == NULL)
  221.     {
  222.       return false;
  223.     }
  224.     unz_global_info* pGlobalInfo = new unz_global_info;
  225.     nReturnValue = unzGetGlobalInfo(unzfile, pGlobalInfo);
  226.     if (nReturnValue != UNZ_OK)
  227.     {
  228.       return false;
  229.     }
  230.     unz_file_info* pFileInfo = new unz_file_info;
  231.     char szZipFName[MAX_PATH] = { 0 };
  232.     char szExtraName[MAX_PATH] = { 0 };
  233.     char szCommName[MAX_PATH] = { 0 };
  234.     for (int i = 0; i < pGlobalInfo->number_entry; i++)
  235.     {
  236.       nReturnValue = unzGetCurrentFileInfo(unzfile, pFileInfo, szZipFName, MAX_PATH, szExtraName, MAX_PATH, szCommName, MAX_PATH);
  237.       if (nReturnValue != UNZ_OK)
  238.         return false;
  239.       string strZipFName = szZipFName;
  240.       // 如果是目录,创建相应目录
  241.       if (pFileInfo->external_fa == FILE_ATTRIBUTE_DIRECTORY || (strZipFName.rfind('/') == strZipFName.length() - 1))
  242.       {
  243.         destFilePath = strTempPath + "//" + szZipFName;
  244.         CreateDirectoryA(destFilePath.c_str(), NULL);
  245.       }
  246.       else
  247.       {
  248.         string strFullFilePath;
  249.         tempFilePath = strTempPath + "/" + szZipFName;
  250.         strFullFilePath = tempFilePath;
  251.         int nPos = tempFilePath.rfind("/");
  252.         int nPosRev = tempFilePath.rfind("\");
  253.         if (nPosRev == string::npos && nPos == string::npos)
  254.           continue;
  255.         size_t nSplitPos = nPos > nPosRev ? nPos : nPosRev;
  256.         destFilePath = tempFilePath.substr(0, nSplitPos + 1);
  257.         // 创建多级目录
  258.         if (!PathIsDirectoryA(destFilePath.c_str()))
  259.         {
  260.           destFilePath = replace_all(destFilePath, "/", "\");
  261.           int bRet = CreatedMultipleDirectory(destFilePath);
  262.         }
  263.         strFullFilePath = replace_all(strFullFilePath, "/", "\");
  264.         // 创建文件并写入数据
  265.         HANDLE hFile = CreateFileA(strFullFilePath.c_str(), GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL);
  266.         if (hFile == INVALID_HANDLE_VALUE)
  267.         {
  268.           return false;
  269.         }
  270.         nReturnValue = unzOpenCurrentFile(unzfile);
  271.         if (nReturnValue != UNZ_OK)
  272.         {
  273.           CloseHandle(hFile);
  274.           return false;
  275.         }
  276.         uLong BUFFER_SIZE = pFileInfo->uncompressed_size;
  277.         void* szReadBuffer = NULL;
  278.         szReadBuffer = (char*)malloc(BUFFER_SIZE);
  279.         if (NULL == szReadBuffer)
  280.         {
  281.           break;
  282.         }
  283.         // 逐块读取ZIP文件并写入目标文件
  284.         while (TRUE)
  285.         {
  286.           memset(szReadBuffer, 0, BUFFER_SIZE);
  287.           int nReadFileSize = 0;
  288.           nReadFileSize = unzReadCurrentFile(unzfile, szReadBuffer, BUFFER_SIZE);
  289.           if (nReadFileSize < 0)
  290.           {
  291.             unzCloseCurrentFile(unzfile);
  292.             CloseHandle(hFile);
  293.             return false;
  294.           }
  295.           else if (nReadFileSize == 0)
  296.           {
  297.             unzCloseCurrentFile(unzfile);
  298.             CloseHandle(hFile);
  299.             break;
  300.           }
  301.           else
  302.           {
  303.             DWORD dWrite = 0;
  304.             BOOL bWriteSuccessed = WriteFile(hFile, szReadBuffer, BUFFER_SIZE, &dWrite, NULL);
  305.             if (!bWriteSuccessed)
  306.             {
  307.               unzCloseCurrentFile(unzfile);
  308.               CloseHandle(hFile);
  309.               return false;
  310.             }
  311.           }
  312.         }
  313.         free(szReadBuffer);
  314.       }
  315.       unzGoToNextFile(unzfile);
  316.     }
  317.     delete pFileInfo;
  318.     delete pGlobalInfo;
  319.     if (unzfile)
  320.     {
  321.       unzClose(unzfile);
  322.     }
  323.     return true;
  324.   }
  325. };
复制代码
如何使用类

压缩文件时可以通过调用zip.Compress()函数实现,该函数接受3个参数,第一个参数是需要压缩的目录名,第二个参数是压缩后保存的文件名,第三个参数则是压缩后主目录的名字,我们以压缩D:\\csdn目录下的所有文件为例,代码如下所示;
[code]int main(int argc, char* argv[]){        MyZip zip;        // 压缩目录        std::string compress_src = "D:\\csdn";                               // 压缩目录        std::string compress_dst = "D:\\test.zip";                           // 压缩后        bool compress_flag = zip.Compress(compress_src, compress_dst, "lyshark");        std::cout
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

守听

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表