C++ MiniZip实现目录压缩与解压的示例详解
作者:微软技术分享
Zlib是一个开源的数据压缩库,提供了一种通用的数据压缩和解压缩算法。它最初由Jean-Loup Gailly
和Mark 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.lib
和zlibwapi.lib
如下图;
接着配置引用目录,这里需要多配置一个minizip
头文件,该头文件是zlib里面的一个子项目。
lib库则需要包含zlibstat.lib
和zlibwapi.lib
这两个文件,此处读者可以自行放入到一个目录下;
ZIP 递归压缩目录
如下所示代码是一个使用zlib库实现的简单文件夹压缩工具的C++程序。该程序提供了压缩文件夹到 ZIP 文件的功能,支持递归地添加文件和子文件夹,利用了 Windows API 和 zlib 库的函数。
#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") bool nyAddfiletoZip(zipFile zfile, const std::string& fileNameinZip, const std::string& srcfile) { // 目录如果为空则直接返回 if (NULL == zfile || fileNameinZip.empty()) { return 0; } 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, "\\"); } nErr = zipOpenNewFileInZip(zfile, sznewfileName, &zinfo, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION); if (nErr != ZIP_OK) { return false; } if (!srcfile.empty()) { // 打开源文件 FILE* srcfp = _fsopen(srcfile.c_str(), "rb", _SH_DENYNO); if (NULL == srcfp) { std::cout << "打开源文件失败" << std::endl; return false; } // 读入源文件写入zip文件 int numBytes = 0; char* pBuf = new char[1024 * 100]; if (NULL == pBuf) { std::cout << "新建缓冲区失败" << std::endl; return 0; } 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); } zipCloseFileInZip(zfile); return true; } 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 // 生成zip文件中的相对路径 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); } nyAddfiletoZip(zfile, relativepath, szTemp); } while (::FindNextFileA(hFile, &findFileData)); FindClose(hFile); return true; } /* * 函数功能 : 压缩文件夹到目录 * 备 注 : dirpathName 源文件/文件夹 * zipFileName 目的压缩包 * parentdirName 压缩包内名字(文件夹名) */ bool nyCreateZipfromDir(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName) { bool bRet = false; /* APPEND_STATUS_CREATE 创建追加 APPEND_STATUS_CREATEAFTER 创建后追加(覆盖方式) APPEND_STATUS_ADDINZIP 直接追加 */ zipFile zFile = NULL; 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) { std::cout << "创建ZIP文件失败" << std::endl; return bRet; } if (nyCollectfileInDirtoZip(zFile, dirpathName, parentdirName)) { bRet = true; } zipClose(zFile, NULL); return bRet; }
主要功能
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 文件。
示例用法
int main(int argc, char* argv[]) { std::string dirpath = "D:\\lyshark\\test"; // 源文件/文件夹 std::string zipfileName = "D:\\lyshark\\test.zip"; // 目的压缩包 bool ref = nyCreateZipfromDir(dirpath, zipfileName, "lyshark"); // 包内文件名<如果为空则压缩时不指定目录> std::cout << "[LyShark] 压缩状态 " << ref << std::endl; system("pause"); return 0; }
上述调用代码,参数1指定为需要压缩的文件目录,参数2指定为需要压缩成目录名,参数3为压缩后该目录的名字。
ZIP 递归解压目录
在这个C++程序中,实现了递归解压缩ZIP文件的功能。程序提供了以下主要功能:
- replace_all 函数: 用于替换字符串中的指定子串。
- CreatedMultipleDirectory 函数: 用于创建多级目录,确保解压缩时的目录结构存在。
- UnzipFile 函数: 用于递归解压缩 ZIP 文件。该函数打开 ZIP 文件,获取文件信息,然后逐个解析和处理 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") // 将字符串内的old_value替换成new_value 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; } /* * 函数功能 : 递归解压文件目录 * 备 注 : strFilePath 压缩包路径 * strTempPath 解压到 */ void UnzipFile(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; } // 获取zip文件的信息 unz_global_info* pGlobalInfo = new unz_global_info; nReturnValue = unzGetGlobalInfo(unzfile, pGlobalInfo); if (nReturnValue != UNZ_OK) { std::cout << "数据包: " << pGlobalInfo->number_entry << endl; return; } // 解析zip文件 unz_file_info* pFileInfo = new unz_file_info; char szZipFName[MAX_PATH] = { 0 }; char szExtraName[MAX_PATH] = { 0 }; char szCommName[MAX_PATH] = { 0 }; // 存放从zip中解析出来的内部文件名 for (int i = 0; i < pGlobalInfo->number_entry; i++) { // 解析得到zip中的文件信息 nReturnValue = unzGetCurrentFileInfo(unzfile, pFileInfo, szZipFName, MAX_PATH, szExtraName, MAX_PATH, szCommName, MAX_PATH); if (nReturnValue != UNZ_OK) return; std::cout << "解压文件名: " << szZipFName << endl; 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; } // 打开文件 nReturnValue = unzOpenCurrentFile(unzfile); if (nReturnValue != UNZ_OK) { CloseHandle(hFile); return; } // 读取文件 uLong BUFFER_SIZE = pFileInfo->uncompressed_size;; void* szReadBuffer = NULL; szReadBuffer = (char*)malloc(BUFFER_SIZE); if (NULL == szReadBuffer) { break; } while (TRUE) { memset(szReadBuffer, 0, BUFFER_SIZE); int nReadFileSize = 0; nReadFileSize = unzReadCurrentFile(unzfile, szReadBuffer, BUFFER_SIZE); // 读取文件失败 if (nReadFileSize < 0) { unzCloseCurrentFile(unzfile); CloseHandle(hFile); return; } // 读取文件完毕 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; } } } free(szReadBuffer); } unzGoToNextFile(unzfile); } delete pFileInfo; delete pGlobalInfo; // 关闭 if (unzfile) { unzClose(unzfile); } }
主要功能
replace_all 函数
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 函数
BOOL CreatedMultipleDirectory(const std::string& direct)
功能:创建多级目录,确保路径存在。
参数:
- direct:目录路径。
- 返回值:如果成功创建目录返回 TRUE,否则返回 FALSE。
UnzipFile 函数
void UnzipFile(const std::string& strFilePath, const std::string& strTempPath)
功能:递归解压缩 ZIP 文件。
参数:
- strFilePath:ZIP 文件路径。
- strTempPath:解压到的目标路径。
该函数打开 ZIP 文件,获取文件信息,然后逐个解析和处理 ZIP 文件中的文件或目录。在解析过程中,根据文件或目录的属性,创建相应的目录结构,然后将文件写入目标路径。
示例用法
int main(int argc, char* argv[]) { std::string srcFilePath = "D:\\lyshark\\test.zip"; std::string tempdir = "D:\\lyshark\\test"; // 如果传入目录不存在则创建 if (!::PathFileExistsA(tempdir.c_str())) { CreatedMultipleDirectory(tempdir); } // 调用解压函数 UnzipFile(srcFilePath, tempdir); system("pause"); return 0; }
案例中,首先在解压缩之前判断传入目录是否存在,如果不存在则需要调用API创建目录,如果存在则直接调用UnzipFIle
解压缩函数,实现解包,输出效果图如下;
到此这篇关于C++ MiniZip实现目录压缩与解压的示例详解的文章就介绍到这了,更多相关C++ MiniZip目录压缩与解压内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!