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

news2024/12/29 9:57:36

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

在软件开发中,文件的压缩和解压缩是一项常见的任务,而ZIP是一种被广泛应用的压缩格式。为了方便地处理ZIP压缩和解压缩操作,开发者通常使用各种编程语言和库来实现这些功能。本文将聚焦于一个简化的C++实现,通过分析代码,我们将深入了解其设计和实现细节。

类的功能实现

MyZip类旨在提供简单易用的ZIP压缩和解压缩功能。通过成员函数CompressUnCompress,该类使得对目录的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压缩和解压缩功能,并可以在项目中被应用,该类代码如下所示;

#define ZLIB_WINAPI
#include <string>
#include <iostream>
#include <vector>
#include <Shlwapi.h> 
#include <zip.h>
#include <unzip.h>
#include <zlib.h>

using namespace std;

#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "zlibstat.lib")

class MyZip
{
private:
  // 向ZIP文件中添加文件
  bool nyAddfiletoZip(zipFile zfile, const std::string& fileNameinZip, const std::string& srcfile)
  {
    if (NULL == zfile || fileNameinZip.empty())
    {
      return false;
    }

    int nErr = 0;
    zip_fileinfo zinfo = { 0 };
    tm_zip tmz = { 0 };
    zinfo.tmz_date = tmz;
    zinfo.dosDate = 0;
    zinfo.internal_fa = 0;
    zinfo.external_fa = 0;

    // 构建新文件名
    char sznewfileName[MAX_PATH] = { 0 };
    memset(sznewfileName, 0x00, sizeof(sznewfileName));
    strcat_s(sznewfileName, fileNameinZip.c_str());
    if (srcfile.empty())
    {
      strcat_s(sznewfileName, "\\");
    }

    // 在ZIP中打开新文件
    nErr = zipOpenNewFileInZip(zfile, sznewfileName, &zinfo, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION);
    if (nErr != ZIP_OK)
    {
      return false;
    }

    // 如果有源文件,读取并写入ZIP文件
    if (!srcfile.empty())
    {
      FILE* srcfp = _fsopen(srcfile.c_str(), "rb", _SH_DENYNO);
      if (NULL == srcfp)
      {
        return false;
      }

      int numBytes = 0;
      char* pBuf = new char[1024 * 100];
      if (NULL == pBuf)
      {
        return false;
      }

      // 逐块读取源文件并写入ZIP
      while (!feof(srcfp))
      {
        memset(pBuf, 0x00, sizeof(pBuf));
        numBytes = fread(pBuf, 1, sizeof(pBuf), srcfp);
        nErr = zipWriteInFileInZip(zfile, pBuf, numBytes);
        if (ferror(srcfp))
        {
          break;
        }
      }

      delete[] pBuf;
      fclose(srcfp);
    }

    // 关闭ZIP文件中的当前文件
    zipCloseFileInZip(zfile);

    return true;
  }

  // 递归地将目录下的文件添加到ZIP
  bool nyCollectfileInDirtoZip(zipFile zfile, const std::string& filepath, const std::string& parentdirName)
  {
    if (NULL == zfile || filepath.empty())
    {
      return false;
    }

    bool bFile = false;
    std::string relativepath = "";
    WIN32_FIND_DATAA findFileData;

    char szpath[MAX_PATH] = { 0 };
    if (::PathIsDirectoryA(filepath.c_str()))
    {
      strcpy_s(szpath, sizeof(szpath) / sizeof(szpath[0]), filepath.c_str());
      int len = strlen(szpath) + strlen("\\*.*") + 1;
      strcat_s(szpath, len, "\\*.*");
    }
    else
    {
      bFile = true;
      strcpy_s(szpath, sizeof(szpath) / sizeof(szpath[0]), filepath.c_str());
    }

    HANDLE hFile = ::FindFirstFileA(szpath, &findFileData);
    if (NULL == hFile)
    {
      return false;
    }

    do
    {
      // 构建相对路径
      if (parentdirName.empty())
        relativepath = findFileData.cFileName;
      else
        relativepath = parentdirName + "\\" + findFileData.cFileName;

      // 如果是目录,递归处理子目录
      if (findFileData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
      {
        if (strcmp(findFileData.cFileName, ".") != 0 && strcmp(findFileData.cFileName, "..") != 0)
        {
          nyAddfiletoZip(zfile, relativepath, "");

          char szTemp[MAX_PATH] = { 0 };
          strcpy_s(szTemp, filepath.c_str());
          strcat_s(szTemp, "\\");
          strcat_s(szTemp, findFileData.cFileName);
          nyCollectfileInDirtoZip(zfile, szTemp, relativepath);
        }
        continue;
      }

      char szTemp[MAX_PATH] = { 0 };
      if (bFile)
      {
        strcpy_s(szTemp, filepath.c_str());
      }
      else
      {
        strcpy_s(szTemp, filepath.c_str());
        strcat_s(szTemp, "\\");
        strcat_s(szTemp, findFileData.cFileName);
      }

      // 将文件添加到ZIP
      nyAddfiletoZip(zfile, relativepath, szTemp);

    } while (::FindNextFileA(hFile, &findFileData));

    FindClose(hFile);

    return true;
  }

  // 替换字符串中的所有指定子串
  std::string& replace_all(std::string& str, const std::string& old_value, const std::string& new_value)
  {
    while (true)
    {
      std::string::size_type pos(0);
      if ((pos = str.find(old_value)) != std::string::npos)
        str.replace(pos, old_value.length(), new_value);
      else
        break;
    }
    return str;
  }

  // 创建多级目录
  BOOL CreatedMultipleDirectory(const std::string& direct)
  {
    std::string Directoryname = direct;
    if (Directoryname[Directoryname.length() - 1] != '\\')
    {
      Directoryname.append(1, '\\');
    }

    std::vector< std::string> vpath;
    std::string strtemp;
    BOOL  bSuccess = FALSE;

    // 遍历目录字符串,逐级创建目录
    for (int i = 0; i < Directoryname.length(); i++)
    {
      if (Directoryname[i] != '\\')
      {
        strtemp.append(1, Directoryname[i]);
      }
      else
      {
        vpath.push_back(strtemp);
        strtemp.append(1, '\\');
      }
    }

    std::vector< std::string>::iterator vIter = vpath.begin();
    for (; vIter != vpath.end(); vIter++)
    {
      bSuccess = CreateDirectoryA(vIter->c_str(), NULL) ? TRUE : FALSE;
    }

    return bSuccess;
  }

public:
  // 压缩目录
  bool Compress(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName)
  {
    bool bRet = false;
    zipFile zFile = NULL;

    // 根据ZIP文件是否存在选择打开方式
    if (!::PathFileExistsA(zipfileName.c_str()))
    {
      zFile = zipOpen(zipfileName.c_str(), APPEND_STATUS_CREATE);
    }
    else
    {
      zFile = zipOpen(zipfileName.c_str(), APPEND_STATUS_ADDINZIP);
    }

    if (NULL == zFile)
    {
      return bRet;
    }

    // 将目录下的文件添加到ZIP
    if (nyCollectfileInDirtoZip(zFile, dirpathName, parentdirName))
    {
      bRet = true;
    }

    zipClose(zFile, NULL);

    return bRet;
  }

  // 解压目录
  bool UnCompress(const std::string& strFilePath, const std::string& strTempPath)
  {
    int nReturnValue;
    string tempFilePath;
    string srcFilePath(strFilePath);
    string destFilePath;

    // 打开ZIP文件
    unzFile unzfile = unzOpen(srcFilePath.c_str());
    if (unzfile == NULL)
    {
      return false;
    }

    unz_global_info* pGlobalInfo = new unz_global_info;
    nReturnValue = unzGetGlobalInfo(unzfile, pGlobalInfo);
    if (nReturnValue != UNZ_OK)
    {
      return false;
    }

    unz_file_info* pFileInfo = new unz_file_info;
    char szZipFName[MAX_PATH] = { 0 };
    char szExtraName[MAX_PATH] = { 0 };
    char szCommName[MAX_PATH] = { 0 };

    for (int i = 0; i < pGlobalInfo->number_entry; i++)
    {
      nReturnValue = unzGetCurrentFileInfo(unzfile, pFileInfo, szZipFName, MAX_PATH, szExtraName, MAX_PATH, szCommName, MAX_PATH);
      if (nReturnValue != UNZ_OK)
        return false;

      string strZipFName = szZipFName;

      // 如果是目录,创建相应目录
      if (pFileInfo->external_fa == FILE_ATTRIBUTE_DIRECTORY || (strZipFName.rfind('/') == strZipFName.length() - 1))
      {
        destFilePath = strTempPath + "//" + szZipFName;
        CreateDirectoryA(destFilePath.c_str(), NULL);
      }
      else
      {
        string strFullFilePath;
        tempFilePath = strTempPath + "/" + szZipFName;
        strFullFilePath = tempFilePath;

        int nPos = tempFilePath.rfind("/");
        int nPosRev = tempFilePath.rfind("\\");
        if (nPosRev == string::npos && nPos == string::npos)
          continue;

        size_t nSplitPos = nPos > nPosRev ? nPos : nPosRev;
        destFilePath = tempFilePath.substr(0, nSplitPos + 1);

        // 创建多级目录
        if (!PathIsDirectoryA(destFilePath.c_str()))
        {
          destFilePath = replace_all(destFilePath, "/", "\\");
          int bRet = CreatedMultipleDirectory(destFilePath);
        }

        strFullFilePath = replace_all(strFullFilePath, "/", "\\");

        // 创建文件并写入数据
        HANDLE hFile = CreateFileA(strFullFilePath.c_str(), GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL);
        if (hFile == INVALID_HANDLE_VALUE)
        {
          return false;
        }

        nReturnValue = unzOpenCurrentFile(unzfile);
        if (nReturnValue != UNZ_OK)
        {
          CloseHandle(hFile);
          return false;
        }

        uLong BUFFER_SIZE = pFileInfo->uncompressed_size;
        void* szReadBuffer = NULL;
        szReadBuffer = (char*)malloc(BUFFER_SIZE);
        if (NULL == szReadBuffer)
        {
          break;
        }

        // 逐块读取ZIP文件并写入目标文件
        while (TRUE)
        {
          memset(szReadBuffer, 0, BUFFER_SIZE);
          int nReadFileSize = 0;

          nReadFileSize = unzReadCurrentFile(unzfile, szReadBuffer, BUFFER_SIZE);

          if (nReadFileSize < 0)
          {
            unzCloseCurrentFile(unzfile);
            CloseHandle(hFile);
            return false;
          }
          else if (nReadFileSize == 0)
          {
            unzCloseCurrentFile(unzfile);
            CloseHandle(hFile);
            break;
          }
          else
          {
            DWORD dWrite = 0;
            BOOL bWriteSuccessed = WriteFile(hFile, szReadBuffer, BUFFER_SIZE, &dWrite, NULL);
            if (!bWriteSuccessed)
            {
              unzCloseCurrentFile(unzfile);
              CloseHandle(hFile);
              return false;
            }
          }
        }

        free(szReadBuffer);
      }
      unzGoToNextFile(unzfile);
    }

    delete pFileInfo;
    delete pGlobalInfo;

    if (unzfile)
    {
      unzClose(unzfile);
    }

    return true;
  }
};

如何使用类

压缩文件时可以通过调用zip.Compress()函数实现,该函数接受3个参数,第一个参数是需要压缩的目录名,第二个参数是压缩后保存的文件名,第三个参数则是压缩后主目录的名字,我们以压缩D:\\csdn目录下的所有文件为例,代码如下所示;

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 << "压缩状态: " << compress_flag << std::endl;

	system("pause");
	return 0;
}

压缩后可以看到对应的压缩包内容,如下所示;

解压缩与压缩类似,通过调用zip.UnCompress实现,该方法需要传入两个参数,被压缩的文件名和解压到的目录名,如果目录不存在则会创建并解压。

int main(int argc, char* argv[])
{
	MyZip zip;

	// 解压缩目录
	std::string uncompress_src = "D:\\test.zip";                      // 被解压文件
	std::string uncompress_dst = "D:\\dst";                           // 解压到

	bool compress_flag = zip.UnCompress(uncompress_src, uncompress_dst);
	std::cout << "解压缩状态: " << compress_flag << std::endl;

	system("pause");
	return 0;
}

输出效果如下所示;

文章转载自:微软技术分享

原文链接:https://www.cnblogs.com/LyShark/p/17865137.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1271490.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《如何戒掉坏习惯》精华摘抄

“劣质货币会驱逐优良货币。”这就是格雷沙姆法则&#xff08;劣币驱逐良币 法则&#xff09;。它是指一旦对劣质货币&#xff08;假币&#xff09;的流通放任不管&#xff0c;人们就不会 去使用优良货币&#xff08;真正的货币&#xff09;&#xff0c;从而导致优良货币从市场…

深入探索Maven:优雅构建Java项目的新方式(二)

Meven高级 1&#xff0c;属性1.1 属性1.1.1 问题分析1.1.2 解决步骤步骤1:父工程中定义属性步骤2:修改依赖的version 1.2 配置文件加载属性步骤1:父工程定义属性步骤2:jdbc.properties文件中引用属性步骤3:设置maven过滤文件范围步骤4:测试是否生效 1.3 版本管理 2&#xff0c;…

todesk连接ubuntu显示当前系统并无桌面环境,或无显示器,无法显示远程桌面,您需要自行安装X11桌面环境,或者使用终端文件功能

ToDesk远程遇到的问题如上图&#xff0c;换向日葵直接黑屏&#xff1b; 问题原因 截止发文时间&#xff0c;Todesk只支持X11协议&#xff0c;没有适配最新的Wayland协议&#xff0c;所以我们需要把窗口系统调整为X11才可以。 解决方法 修改配置文件&#xff0c;关闭wayland su…

Hadoop入门学习笔记

视频课程地址&#xff1a;https://www.bilibili.com/video/BV1WY4y197g7 课程资料链接&#xff1a;https://pan.baidu.com/s/15KpnWeKpvExpKmOC8xjmtQ?pwd5ay8 这里写目录标题 一、VMware准备Linux虚拟机1.1. VMware安装Linux虚拟机1.1.1. 修改虚拟机子网IP和网关1.1.2. 安装…

AI PC专题:AI PC深入变革PC产业

今天分享的是AI系列深度研究报告&#xff1a;《AI PC专题&#xff1a;AI PC深入变革PC产业》。 &#xff08;报告出品方&#xff1a;西南证券研究发展中心&#xff09; 报告共计&#xff1a;30页 AI PC将深入变革PC产业  从出货量看&#xff0c;PC整体呈现周期性的特征。2…

智慧灯杆网关:引领城市智慧照明的未来

智慧灯杆网关&#xff0c;作为城市智慧照明系统的核心组件&#xff0c;正逐渐成为各大城市发展的关键所在。它的出现使得城市照明管理更加智能、高效&#xff0c;为未来城市的可持续发展奠定了坚实的基础。 智慧灯杆网关是一种集网络通信、数据处理、远程控制等功能于一体的设备…

系统部署安装-Centos7-Cassandra

文章目录 介绍安装在线下载安装启动普通启动注册服务 介绍 Apache Cassandra是一个高度可扩展的高性能分布式数据库&#xff0c;旨在处理许多商用服务器上的大量数据&#xff0c;提供高可用性而没有单点故障。 安装 在线下载 &#xff08;1&#xff09;使用weget下载最新的…

火锅店管理系统扫码点餐小程序作用如何

火锅店在餐饮行业中占据了很高地位&#xff0c;受众非常广&#xff0c;当然火热的赛道自然少不了众多品牌竞争&#xff0c;由于火锅店有一定成本支出&#xff0c;所以商家更希望能加大生意营收&#xff0c;而强到店属性下&#xff0c;如何将客户饮料进店和赋能客户消费就变得很…

天眼销:精准的企业名录

企业名录的重要性&#xff0c;对于销售而言都是极其重要的。本期为家人们分享如何正确挑选出优质的企业名录渠道&#xff0c;避免走一些弯弯坑坑。 为了有效利用企业名录进行客户开发&#xff0c;您需要关注信息的准确性、可提供的资源数量以及信息的时效性。能否根据您的需求…

封装进度条onUploadProgress+axios取消请求的上传组件

目录 定时模拟进度条 方法 A.axios B.xhr 取消请求 完整代码 A.自定义上传组件 B.二次封装组件 情况 增加cancelToken不生效&#xff0c;刷新页面 进度条太快->设置浏览器网速 定时模拟进度条 startUpload() {if (!this.file) return;const totalSize this.fil…

土壤养分分析仪:精准农业,从“土”开始

在农业生产中&#xff0c;土壤的质量是决定农作物产量和品质的关键因素。然而&#xff0c;传统的土壤检测方法耗时费力&#xff0c;且结果往往不够准确。随着科技的发展&#xff0c;土壤养分分析仪为现代农业带来了新的可能。 土壤养分分析仪是一种专门用于测量土壤中各种养分含…

Flutter应用程序加固的问题及解决方案

​&#x1f680;Flutter应用程序加固的问题及解决方案引言在移动应用开发中&#xff0c;为了保护应用程序的安全性&#xff0c;开发者需要对应用进行加固。在使用Flutter技术进行应用程序开发时&#xff0c;也需要注意应用程序的安全问题和加固方案。本文将介绍在Flutter应用程…

码云配置遇到秘钥不正确

你这个就是秘钥没有和git绑定&#xff0c; 需要 git config --global user.name "你的用户名随便写" git config --global user.email "你的邮箱"

老师怎么培养班干部

老师除了教学之外&#xff0c;班级管理也是一项重要的任务。而培养班干部则是班级管理中不可或缺的一部分。今天为大家分享一些关于如何培养班干部的干货。 明确职责&#xff0c;制定班规 在选拔班干部之后&#xff0c;首先需要明确各个职位的职责&#xff0c;让每位班干部都清…

22款奔驰GLS450升级HUD抬头显示 平视仪表信息

说起HUD抬头显示这个配置&#xff0c;最初是用在战斗机上的&#xff0c;它可以让战斗机驾驶员读取飞机的各种信息和状态&#xff0c;而无需移动头部&#xff0c;这样就能够有效的提高效率。但随着汽车技术的进步HUD这种配置也逐渐下放到民用车上。发展到今&#xff0c;车上的抬…

一文讲透Python函数中的局部变量和全局变量

变量的作用域就是变量能够发挥作用的区域&#xff0c;超出既定区域后就无法发挥作用。根据变量的作用域可以将变量分为局部变量和全局变量。 1.局部变量 局部变量是在函数内部定义并使用的变量&#xff0c;也就是说只有在函数内部&#xff0c;在函数运行时才会有效&#xff0…

Docker安装可视化工具Portainer

目录 Portainer简介 Portainer安装 Portainer简介 Portainer是一款开源的容器管理平台&#xff0c;支持多种容器技术&#xff0c;如Docker、Kubernetes和Swarm等。它提供了一个易于使用的Web UI界面&#xff0c;可用于管理和监控容器和集群。Portainer旨在使容器管理更加简单…

sqli-labs靶场详解(less38-less45)

堆叠注入 ​ less-38 less-38 ?id1 and 11;%00 成功 ?id1 and 12;%00 失败 不是吧 这就出来了&#xff1f; ?id1 order by 4;%00 报错 4列不行 ?id0 union select 1,2,3;%00 显示位置为2,3 ?id0 union select 1,database(),3;%00 数据库为security ?id0 union select 1,…

HarmonyOS入门开发(三) 持久化存储Preferences

接入鸿蒙几天以来&#xff0c;发现各种和Android不一样的地方&#xff0c;今天来看一下Preferences存储 在Android中比如有ShardPreferences、Mmkv这些持久化存储方式&#xff0c;开发起来很方便&#xff0c;读取速度也很快&#xff0c;在鸿蒙里面也提供了对应的持久化存储方案…

plist文件在线生成网页配置苹果ios系统ipa文件下载

您可以进入首页—工具箱—plist文件在线制作 您可以进入控制台—plist文件 ●也可以直接访问&#xff1a;咕噜分发内测平台-苹果ios系统应用安卓apk安全漏洞扫描提供商 ●应用名称_包体的bid-下载地址-图标地址 ●如果不知道怎么查看苹果包名 可以通过咕噜分发【工具箱】-【IOS…