目录
1、使用场景
2、压缩包的创建
3、压缩包的解压
4、CloseZipZ和CloseZipU两接口的区别
5、开源zip.cpp和unzip.cpp文件的下载
有时我们需要在代码中实现压缩包的创建以及对压缩包的解压,开源的win32版本的zip.cpp和unzip.cpp给我们提供了途径和办法,本文我们就来讲讲如何使用这两个开源的cpp文件实现压缩包的创建和解压。
压缩包的解压一般用在自制的安装包(自己实现的安装包程序,不使用InstallShield等打包工具)中。我们一般将要拷贝到安装路径下的文件打成zip包放置到安装包工程的资源中,集成到exe文件中,在执行安装包安装时,将zip压缩包从资源中取出来,解压到磁盘上,然后再将文件拷贝到安装路径中。
为了避免和其他头文件出现函数重名的冲突,我们在两个CPP对应的头文件中添加了命名空间INS_ZIP。我们首先调用CreateZip接口创建一个压缩包,然后调用ZipAdd将文件添加到压缩包中。
如果要将文件夹添加到压缩包中,则需要遍历文件夹中的文件,然后在调用ZipAdd时要把文件夹中的文件相对于压缩包的相对路径传递给ZipAdd。对文件夹的操作,我们已经封装成名叫AddZipFolder的接口。
创建压缩包的代码如下所示,我们将config.ini文件和msssdk文件夹添加到压缩包中:
// 先创建zip包.zip文件,在把各个文件和文件夹添加进去,对于文件夹需要
// 递归遍历
INS_ZIP::HZIP hz = INS_ZIP::CreateZip( strUserZipFile, 0 );
if ( hz == NULL )
{
return;
}
// 压缩包最上层的文件直接调用ZipAdd添加到压缩包中即可
CString strConfigFilePath = _T("D:\\config.ini");
if ( PathFileExists( strConfigFilePath ) )
{
INS_ZIP::ZipAdd( hz, _T("login.ini"), strConfigFilePath );
}
// 如果是待压缩的中间包含文件夹,则在调用函数时,要自己维护文件的
// 层次关系,即加入的文件在当前zip包中应该是相对路径,所以调用
// AddZipFolder在传递绝对路径的同时,还要传递Users的相对路径
CString strZipDir = _T("D:\\msssdk");
if ( PathFileExists( strZipDir ) )
{
AddZipFolder( hz, strZipDir, _T("msssdk") );
}
INS_ZIP::CloseZipZ( hz );
处理文件夹的接口AddZipFolder实现如下:
void AddZipFolder( INS_ZIP::HZIP hz, LPCTSTR lpszDir, LPCTSTR lpRelPath )
{
CString strFindFileName = lpszDir;
strFindFileName += _T("\\*.*");
WIN32_FIND_DATA wfd;
HANDLE hFindFile = FindFirstFile( strFindFileName, &wfd );
if ( hFindFile == INVALID_HANDLE_VALUE )
{
return;
}
CString strFilePath;
CString strRelPath;
while ( 1 )
{
if ( wfd.cFileName[0] != '.' )
{
strFilePath = lpszDir;
strFilePath += _T("\\");
strFilePath += wfd.cFileName;
strRelPath = lpRelPath;
strRelPath += _T("\\");
strRelPath += wfd.cFileName;
if ( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) // 目录
{
// 文件夹要递归调用
AddZipFolder( hz, strFilePath, strRelPath );
// 对于ZipAddFolder的调用,一般传入文加件名。如果文件夹中包含子文件夹,则要传入相对路径
// 注意,不能不能传入绝对路径,因为ZipAddFolder会依次创建绝对路径中的文件。因为文件夹中
// 包含子文件夹,比如msssdk\include,会先遍历到最上面的文件夹Users,
// 在压缩包中线创建msssdk目录,当遍历到include,传入msssdk\include,由于父目录Users已经
// 存在,所以只创建include目录
// 相对路径,即加入的文件相对zip包的相对路径
ZipAddFolder( hz, strRelPath );
}
else // 文件
{
// ZipAdd添加文件,要保证在压缩包中的相对位置,所以要传入相对路径strRelPath,最后一个参
// 数则要传入待打包文件的完整路径
ZipAdd( hz, (LPCTSTR)strRelPath, (LPCTSTR)strFilePath );
}
}
if ( !FindNextFile( hFindFile, &wfd ) )
{
break;
}
}
FindClose( hFindFile );
}
先调用OpenZip打开压缩包,然后调用GetZipItem获取压缩包中文件的个数,然后再for循环依次调用GetZipItem获取文件信息,依次调用UnzipItem将每个文件都解压出来,相关代码如下所示:
// 返回TRUE表示没有错误,返回FALSE表示有错误发生,
// strDstPath:解压出来的文件的存放路径,strZipName待解压的压缩包文件名(含绝对路径)
BOOL Unzip( CString strDstPath, CString strZipName )
{
BOOL bExistError = FALSE;
SetCurrentDirectory( strDstPath );
INS_UNZIP::HZIP hz = INS_UNZIP::OpenZip( strZipName, 0 );
if ( hz == NULL )
{
return FALSE;
}
INS_UNZIP::ZIPENTRY ze;
INS_UNZIP::GetZipItem( hz, -1, &ze );
int numitems = ze.index;
for ( int i = 0; i < numitems; i++ )
{
INS_UNZIP::GetZipItem( hz, i, &ze );
DWORD dwRet = INS_UNZIP::UnzipItem( hz, i, ze.name );
// 解压有错误
if ( dwRet != 0 )
{
CString strLog;
strLog.Format( _T("[Unzip]文件解压错误:%s, lasterror: 0x%08X"), ze.name, dwRet );
WriteLog( strLog );
bExistError = TRUE;
}
}
INS_UNZIP::CloseZipU( hz );
return !bExistError;
}
在创建压缩包和解压压缩包之后,要记得调用关闭压缩包句柄的接口(CloseZipZ和CloseZipU两个接口),不然会有资源泄露。
这个地方要稍微区分一下CloseZipZ和CloseZipU两个接口,这两个接口咋一看上去很容易分不清。其实很简单,CloseZipZ接口的结尾Z对应zip首字母, CloseZipU接口的结尾U对应unzip首字母。
如果是调用CreateZip接口创建压缩包,将调用zip.cpp文件中的接口,关闭句柄时要使用对应的ClozeZipZ接口。
如果是调用OpenZip接口打开压缩包进行解压,将调用unzip.cpp文件中的接口,关闭句柄时要使用对应的ClozeZipU接口。
开源的zip.cpp和unzip.cpp及对应的头文件,可以到网盘中下载:
链接:https://pan.baidu.com/s/1yvFYkEs3Upxfqi2dmKVlQg
提取码:p55w