C++?MiniZip實現(xiàn)目錄壓縮與解壓的示例詳解
Zlib是一個開源的數(shù)據(jù)壓縮庫,提供了一種通用的數(shù)據(jù)壓縮和解壓縮算法。它最初由Jean-Loup Gailly和Mark Adler開發(fā),旨在成為一個高效、輕量級的壓縮庫,其被廣泛應用于許多領域,包括網(wǎng)絡通信、文件壓縮、數(shù)據(jù)庫系統(tǒng)等。其壓縮算法是基于DEFLATE算法,這是一種無損數(shù)據(jù)壓縮算法,通常能夠提供相當高的壓縮比。
在Zlib項目中的contrib目錄下有一個minizip子項目,minizip實際上不是zlib庫的一部分,而是一個獨立的開源庫,用于處理ZIP壓縮文件格式。它提供了對ZIP文件的創(chuàng)建和解壓的簡單接口。minizip在很多情況下與zlib一起使用,因為ZIP壓縮通常使用了DEFLATE壓縮算法。通過對minizip庫的二次封裝則可實現(xiàn)針對目錄的壓縮與解壓功能。

如果你想使用minizip通常你需要下載并編譯它,然后將其鏈接到你的項目中。
編譯Zlib庫很簡單,解壓文件并進入到\zlib-1.3\contrib\vstudio目錄下,根據(jù)自己編譯器版本選擇不同的目錄,這里我選擇vc12,進入后打開zlibvc.sln等待生成即可。

成功后可獲得兩個文件分別是zlibstat.lib和zlibwapi.lib如下圖;

接著配置引用目錄,這里需要多配置一個minizip頭文件,該頭文件是zlib里面的一個子項目。

lib庫則需要包含zlibstat.lib和zlibwapi.lib這兩個文件,此處讀者可以自行放入到一個目錄下;

ZIP 遞歸壓縮目錄
如下所示代碼是一個使用zlib庫實現(xiàn)的簡單文件夾壓縮工具的C++程序。該程序提供了壓縮文件夾到 ZIP 文件的功能,支持遞歸地添加文件和子文件夾,利用了 Windows API 和 zlib 庫的函數(shù)。
#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 << "新建緩沖區(qū)失敗" << 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;
}
/*
* 函數(shù)功能 : 壓縮文件夾到目錄
* 備 注 : dirpathName 源文件/文件夾
* zipFileName 目的壓縮包
* parentdirName 壓縮包內(nèi)名字(文件夾名)
*/
bool nyCreateZipfromDir(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName)
{
bool bRet = false;
/*
APPEND_STATUS_CREATE 創(chuàng)建追加
APPEND_STATUS_CREATEAFTER 創(chuàng)建后追加(覆蓋方式)
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 << "創(chuàng)建ZIP文件失敗" << std::endl;
return bRet;
}
if (nyCollectfileInDirtoZip(zFile, dirpathName, parentdirName))
{
bRet = true;
}
zipClose(zFile, NULL);
return bRet;
}
主要功能
nyCreateZipfromDir函數(shù)
bool nyCreateZipfromDir(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName);
功能:壓縮文件夾到指定的 ZIP 文件。
參數(shù):
- dirpathName:源文件夾路徑。
- zipfileName:目標 ZIP 文件路徑。
- parentdirName:在 ZIP 文件內(nèi)的文件夾名(如果為空則不指定目錄)。
nyCollectfileInDirtoZip 函數(shù)
bool nyCollectfileInDirtoZip(zipFile zfile, const std::string& filepath, const std::string& parentdirName);
功能:遞歸地收集文件夾中的文件,并將它們添加到已打開的 ZIP 文件中。
參數(shù):
- zfile:已打開的 ZIP 文件。
- filepath:文件夾路徑。
- parentdirName:在 ZIP 文件內(nèi)的相對文件夾名。
nyAddfiletoZip 函數(shù)
bool nyAddfiletoZip(zipFile zfile, const std::string& fileNameinZip, const std::string& srcfile);
功能:將指定文件添加到已打開的 ZIP 文件中。
參數(shù):
- zfile:已打開的 ZIP 文件。
- fileNameinZip:在 ZIP 文件內(nèi)的相對文件路徑。
- srcfile:源文件路徑。
程序流程
- 文件夾壓縮參數(shù)設置: 用戶提供源文件夾路徑、目標 ZIP 文件路徑,以及在 ZIP 文件內(nèi)的文件夾名。
- ZIP 文件打開: 根據(jù)目標 ZIP 文件是否存在,使用 zipOpen 函數(shù)打開 ZIP 文件。
- 文件夾遞歸添加: 使用 nyCollectfileInDirtoZip 函數(shù)遞歸地收集文件夾中的文件,并通過 nyAddfiletoZip 函數(shù)將它們添加到 ZIP 文件中。
- ZIP 文件關閉: 使用 zipClose 函數(shù)關閉 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"); // 包內(nèi)文件名<如果為空則壓縮時不指定目錄>
std::cout << "[LyShark] 壓縮狀態(tài) " << ref << std::endl;
system("pause");
return 0;
}
上述調(diào)用代碼,參數(shù)1指定為需要壓縮的文件目錄,參數(shù)2指定為需要壓縮成目錄名,參數(shù)3為壓縮后該目錄的名字。

ZIP 遞歸解壓目錄
在這個C++程序中,實現(xiàn)了遞歸解壓縮ZIP文件的功能。程序提供了以下主要功能:
- replace_all 函數(shù): 用于替換字符串中的指定子串。
- CreatedMultipleDirectory 函數(shù): 用于創(chuàng)建多級目錄,確保解壓縮時的目錄結構存在。
- UnzipFile 函數(shù): 用于遞歸解壓縮 ZIP 文件。該函數(shù)打開 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")
// 將字符串內(nèi)的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;
}
// 創(chuàng)建多級目錄
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;
}
/*
* 函數(shù)功能 : 遞歸解壓文件目錄
* 備 注 : 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 << "數(shù)據(jù)包: " << 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中解析出來的內(nèi)部文件名
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;
// 如果是目錄則執(zhí)行創(chuàng)建遞歸目錄名
if (pFileInfo->external_fa == FILE_ATTRIBUTE_DIRECTORY || (strZipFName.rfind('/') == strZipFName.length() - 1))
{
destFilePath = strTempPath + "http://" + szZipFName;
CreateDirectoryA(destFilePath.c_str(), NULL);
}
// 如果是文件則解壓縮并創(chuàng)建
else
{
// 創(chuàng)建文件 保存完整路徑
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()))
{
// 將路徑格式統(tǒng)一
destFilePath = replace_all(destFilePath, "/", "\\");
// 創(chuàng)建多級目錄
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;
}
// 寫入讀取的內(nèi)容
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 函數(shù)
std::string& replace_all(std::string& str, const std::string& old_value, const std::string& new_value)
功能:在字符串 str 中替換所有的 old_value 為 new_value。
參數(shù):
- str:待處理的字符串。
- old_value:要被替換的子串。
- new_value:替換后的新子串。
返回值:替換后的字符串。
CreatedMultipleDirectory 函數(shù)
BOOL CreatedMultipleDirectory(const std::string& direct)
功能:創(chuàng)建多級目錄,確保路徑存在。
參數(shù):
- direct:目錄路徑。
- 返回值:如果成功創(chuàng)建目錄返回 TRUE,否則返回 FALSE。
UnzipFile 函數(shù)
void UnzipFile(const std::string& strFilePath, const std::string& strTempPath)
功能:遞歸解壓縮 ZIP 文件。
參數(shù):
- strFilePath:ZIP 文件路徑。
- strTempPath:解壓到的目標路徑。
該函數(shù)打開 ZIP 文件,獲取文件信息,然后逐個解析和處理 ZIP 文件中的文件或目錄。在解析過程中,根據(jù)文件或目錄的屬性,創(chuàng)建相應的目錄結構,然后將文件寫入目標路徑。
示例用法
int main(int argc, char* argv[])
{
std::string srcFilePath = "D:\\lyshark\\test.zip";
std::string tempdir = "D:\\lyshark\\test";
// 如果傳入目錄不存在則創(chuàng)建
if (!::PathFileExistsA(tempdir.c_str()))
{
CreatedMultipleDirectory(tempdir);
}
// 調(diào)用解壓函數(shù)
UnzipFile(srcFilePath, tempdir);
system("pause");
return 0;
}
案例中,首先在解壓縮之前判斷傳入目錄是否存在,如果不存在則需要調(diào)用API創(chuàng)建目錄,如果存在則直接調(diào)用UnzipFIle解壓縮函數(shù),實現(xiàn)解包,輸出效果圖如下;

到此這篇關于C++ MiniZip實現(xiàn)目錄壓縮與解壓的示例詳解的文章就介紹到這了,更多相關C++ MiniZip目錄壓縮與解壓內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
MFC實現(xiàn)在文件尾追加數(shù)據(jù)的方法
這篇文章主要介紹了MFC實現(xiàn)在文件尾追加數(shù)據(jù)的方法,涉及MFC文件操作的相關技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-09-09
C語言的動態(tài)內(nèi)存分配及動態(tài)內(nèi)存分配函數(shù)詳解
這篇文章主要為大家詳細介紹了C語言的動態(tài)內(nèi)存分配及動態(tài)內(nèi)存分配函數(shù),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-03-03
C++使用expected實現(xiàn)優(yōu)雅的錯誤處理
C++ 中提供了很多中方式進行錯誤處理。無論是通過拋異常還是通過錯誤碼,標準庫都提供相應的調(diào)用,今天本文為大家介紹的是使用expected進行錯誤處理,感興趣的可以了解一下2023-06-06

