C/C++ Zlib 库封装 MyZip 压缩类
- 2023-11-30 福建
本文字数:6025 字
阅读完需:约 20 分钟
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 压缩和解压缩功能,并可以在项目中被应用,该类代码如下所示;
#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;
}
输出效果如下所示;
文章转载自:微软技术分享
不在线第一只蜗牛
还未添加个人签名 2023-06-19 加入
还未添加个人简介
评论