C++ 使用zlib开源库的minizip解压缩文件及文件夹

首先 GitHub 上下载 zlib的发行版本进行编译;

如果出现 zlib报“LNK2001:无法解析的外部符号”错误 的情况,是因为编译zlib(dll)的工程默认有预处理器定义ZLIB_WINAPI,ZEXPORT在zconf.h中的定义为WINAPI:

/* If building or using zlib with the WINAPI/WINAPIV calling convention,
* define ZLIB_WINAPI.
* Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
*/
# ifdef ZLIB_WINAPI
# ifdef FAR
# undef FAR
# endif
# include <windows.h>
/* No need for _export, use ZLIB.DEF instead. */
/* For complete Windows compatibility, use WINAPI, not __stdcall. */
# define ZEXPORT WINAPI
# ifdef WIN32
# define ZEXPORTVA WINAPIV
# else
# define ZEXPORTVA FAR CDECL
# endif
# endif

#define WINAPI      __stdcall
所以,zlib(dll)的导出函数调用约定为__stdcall,和zlib(dll)的导入函数调用约定__cdecl不一致。

针对zlib编译dll文件编译出错,有以下两种解决办法:

1. 在zlivc工程中去掉预处理器定义ZLIB_WINAPI(预处理定义:vs工程属性->C/C++->预处理器)

去掉该定义后,宏定义ZEXPORT在zconf.h文件中实际定义如下:

#ifndef ZEXPORT
#  define ZEXPORT
#endif
// 所以导出函数unzGoToNextFile
extern int ZEXPORT unzGoToNextFile (unzFile  file)

// 等价于
extern int unzGoToNextFile (unzFile  file)

// 也等价于(vs默认是__cdecl函数调用约定)
extern int __cdecl unzGoToNextFile (unzFile  file)

所以在新的工程中导入zlib相关的库文件和dll文件后,编译dll的工程和引入dll的工程的函数调用约定均为__cdecl方式,导出函数能够正确解析了,编译自然就通过了。由上宏定义注释可知,标准编译ZLIB1.DLL是没有ZLIB_WINAPI预处理定义的,函数调用约定也是__cdecl,所以这里推荐该解决方案。

2. 在引入zlib工程中添加预处理器定义ZLIB_WINAPI

在引入dll工程中添加ZLIB_WINAPI定义后,宏定义ZEXPORT在zconf.h文件中实际定义如下:

#define ZEXPORT WINAPI

#define WINAPI  __stdcall

与导出zlib的dll工程均为__stdcall函数调用约定,编译也能顺利通过。

 

zlibHelper.h

#pragma once
#include <iostream>
#include <string>
#include "zip.h"
#include "unzip.h"
#include "zlib.h"

class CZlibHelper
{
public:
	CZlibHelper();
	~CZlibHelper();
	//主要的压缩函数
	//将文件添加到zip文件,若zip文件已存在,则覆盖该文件
	static bool CreateZipfromDir(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName);
	//将文件添加到zip文件,若该zip文件不存在则创建一个zip文件
	static bool CreateZipfromDir2(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName);
	//主要的解压函数
	static bool UnzipFile(const std::string& strFilePath, const std::string& strTempPath);

private:
	//添加文件和文件夹到
	static bool AddfiletoZip(zipFile zfile, const std::string& fileNameinZip, const std::string& srcfile);

	static bool CollectfileInDirtoZip(zipFile zfile, const std::string& filepath, const std::string& parentdirName);
	//字符串 字符替换功能函数
	static std::string& replace_all(std::string& str, const std::string& old_value, const std::string& new_value);
	//创建多级目录
	static bool CreatedMultipleDirectory(const std::string& direct);
};

zlibHelper.cpp

#include "stdafx.h"
#include <vector>
#include <fstream>
#include <sstream>
#include <Shlwapi.h> 

//#include "zip.h"
//#include "unzip.h"
//#include "zlib.h"

#include "zlibHelper.h"

using namespace std;
#pragma comment(lib, "Shlwapi.lib")

CZlibHelper::CZlibHelper()
{
}

CZlibHelper::~CZlibHelper()
{
}

/*
* 函    数 :AddfiletoZip
* 函数功能 :添加文件到要打包的zipFile对象的文件里
* 参数备注 :参数zfile           表示zipFile文件对象
*			参数fileNameinZip   表示要压缩的文件路径
*			参数srcfile         表示要压缩的文件(为空表示为空目录)
*/
bool CZlibHelper::AddfiletoZip(zipFile zfile, const std::string& fileNameinZip, const std::string& srcfile)
{
	if (NULL == zfile || fileNameinZip.empty()/* || srcfile.empty()为空代表空目录*/)
	{
		OutputDebugString("ZKTeco-AddfiletoZip--压缩路径为空");
		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)
	{
		std::string strLog = "ZKTeco-AddfiletoZip--zipOpenNewFileInZip failed! error = ";
		strLog += std::to_string(nErr);
		OutputDebugString(strLog.c_str());
		return false;
	}
	if (!srcfile.empty())
	{
		//打开源文件
		FILE* srcfp = _fsopen(srcfile.c_str(), "rb", _SH_DENYNO);
		if (NULL == srcfp)
		{
			//std::cout << "Open source file failed." << std::endl;
			std::string strLog = "ZKTeco-AddfiletoZip--Open source file failed. error = ";
			int nErr = GetLastError();
			strLog += std::to_string(nErr);
			OutputDebugString(strLog.c_str());
			return false;
		}
		//读入源文件写入zip文件
		int numBytes = 0;
		char* pBuf = new char[1024 * 100];
		if (NULL == pBuf)
		{
			//std::cout << "new buffer failed." << std::endl;
			std::string strLog = "ZKTeco-AddfiletoZip--New buffer failed. error = ";
			int nErr = GetLastError();
			strLog += std::to_string(nErr);
			OutputDebugString(strLog.c_str());
			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;
			}
		}
		if (pBuf)
		{
			delete[] pBuf;
			pBuf = NULL;
		}
		if (srcfp)
		{
			fclose(srcfp);
			srcfp = NULL;
		}
	}
	zipCloseFileInZip(zfile);
	return true;
}

/*
* 函    数 :CollectfileInDirtoZip
* 函数功能 :添加文件到要打包的zipFile对象的文件里
* 参数备注 :参数zfile           表示zipFile文件对象
*			参数fileNameinZip   表示要压缩的文件路径
*			参数srcfile         表示要压缩的文件(为空表示为空目录)
*/
bool CZlibHelper::CollectfileInDirtoZip(zipFile zfile, const std::string& filepath, const std::string& parentdirName)
{
	if (NULL == zfile || filepath.empty())
	{
		printf("zfile or filepath are null.");
		OutputDebugString("ZKTeco-CollectfileInDirtoZip--zfile or filepath are null.");	
		return false;
	}
	bool bFile = false;
	std::string relativepath = "";
	WIN32_FIND_DATAA findFileData;
	memset(&findFileData, 0x0, sizeof(WIN32_FIND_DATAA));
	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)
	{
		printf("FindFirstFile failed.");
		std::string strLog = "ZKTeco-CollectfileInDirtoZip--FindFirstFile failed. error = ";
		int nErr = GetLastError();
		strLog += std::to_string(nErr);
		OutputDebugString(strLog.c_str());
		return false;
	}
	do
	{
		if (parentdirName.empty())
		{
			relativepath = findFileData.cFileName;
		}
		else
		{
			relativepath = parentdirName + "\\" + findFileData.cFileName;//生成zip文件中的相对路径
		}
			
		if (findFileData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
		{
			//过滤文件夹
			if (strcmp(findFileData.cFileName, ".") != 0 && strcmp(findFileData.cFileName, "..") != 0)
			{
				AddfiletoZip(zfile, relativepath, "");
				char szTemp[MAX_PATH] = { 0 };
				strcpy_s(szTemp, filepath.c_str());
				strcat_s(szTemp, "\\");
				strcat_s(szTemp, findFileData.cFileName);
				CollectfileInDirtoZip(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);
		}
		AddfiletoZip(zfile, relativepath, szTemp);
	} while (::FindNextFileA(hFile, &findFileData));
	FindClose(hFile);
	return true;
}
/*
* 压缩函数 :CreateZipfromDir
* 函数功能 :压缩zip、rar文件
* 参数备注 :参数dirpathName   表示要压缩的文件路径
*			参数zipfileName   表示要压缩的文件名称
*			参数parentdirName 表示要压缩的文件创建父目录(为空代表空目录,不创建父目录)
*/
bool CZlibHelper::CreateZipfromDir(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName)
{
	bool bRet = false;

	/* zipOpen 参数注释
	* 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);
		zFile = zipOpen(zipfileName.c_str(), APPEND_STATUS_CREATEAFTER);
	}
	if (NULL == zFile)
	{
		std::cout << "create zip file failed." << std::endl;
		OutputDebugString("ZKTeco-CreateZipfromDir--zipOpen failed.");
		return bRet;
	}
	if (CollectfileInDirtoZip(zFile, dirpathName, parentdirName))
	{
		bRet = true;
	}
	zipClose(zFile, NULL);
	return bRet;
}

/*
* 压缩函数 :CreateZipfromDir
* 函数功能 :压缩zip、rar文件
* 参数备注 :参数dirpathName   表示要压缩的文件路径
*			参数zipfileName   表示要压缩的文件名称
*			参数parentdirName 表示要压缩的文件创建父目录(为空代表空目录,不创建父目录)
*/
bool CZlibHelper::CreateZipfromDir2(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName)
{
	bool bRet = false;

	/* zipOpen 参数注释
	* 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);
		//zFile = zipOpen(zipfileName.c_str(), APPEND_STATUS_CREATEAFTER);
	}
	if (NULL == zFile)
	{
		//std::cout << "create zip file failed." << std::endl;
		OutputDebugString("ZKTeco-CreateZipfromDir--zipOpen failed.");
		return bRet;
	}
	if (CollectfileInDirtoZip(zFile, dirpathName, parentdirName))
	{
		bRet = true;
	}
	zipClose(zFile, NULL);
	return bRet;
}

bool CZlibHelper::UnzipFile(const std::string& strFilePath, const std::string& strTempPath)
{
	int nReturnValue;
	string tempFilePath;
	string srcFilePath(strFilePath);
	string destFilePath;

	std::cout << "Start unpacking the package... " << endl;
	if (!::PathFileExistsA(strTempPath.c_str()))
	{
		CreatedMultipleDirectory(strTempPath);
	}

	//打开zip文件
	unzFile unzfile = unzOpen(srcFilePath.c_str());
	if (unzfile == NULL)
	{
		printf("unzOpen failed.");
		OutputDebugString("ZKTeco-UnzipFile--unzOpen failed.");
		return false;
	}

	//获取zip文件的信息
	unz_global_info* pGlobalInfo = new unz_global_info;
	nReturnValue = unzGetGlobalInfo(unzfile, pGlobalInfo);
	if (nReturnValue != UNZ_OK)
	{
		printf("unzGetGlobalInfo failed.");
		//std::cout << "The number of compressed package files is  " << pGlobalInfo->number_entry << endl;
		std::string strLog = "ZKTeco-UnzipFile--unzGetGlobalInfo failed. error = ";
		strLog += std::to_string(nReturnValue);
		OutputDebugString(strLog.c_str());
		return false;
	}


	//解析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 < (int)pGlobalInfo->number_entry; i++)
	{
		//解析得到zip中的文件信息
		nReturnValue = unzGetCurrentFileInfo(unzfile, pFileInfo, szZipFName, MAX_PATH, szExtraName, MAX_PATH, szCommName, MAX_PATH);
		if (nReturnValue != UNZ_OK)
		{
			printf("unzGetCurrentFileInfo failed.");
			std::string strLog = "ZKTeco-UnzipFile--unzGetCurrentFileInfo failed. error = ";
			strLog += std::to_string(nReturnValue);
			OutputDebugString(strLog.c_str());
			return false;
		}	
		std::cout << "ZipName: " << szZipFName << "  Extra: " << szExtraName << "  Comm: " << szCommName << 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_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL);
			if (hFile == INVALID_HANDLE_VALUE)
			{
				int nErr = GetLastError();
				if (!::PathFileExistsA(strFullFilePath.c_str()))//如果目录已经存在,则跳过,不在重新创建
				{
					return false;
				}
			}
			//打开文件
			nReturnValue = unzOpenCurrentFile(unzfile);
			if (nReturnValue != UNZ_OK)
			{
				CloseHandle(hFile);
				printf("unzOpenCurrentFile failed.");
				std::string strLog = "ZKTeco-UnzipFile--unzOpenCurrentFile failed. error = ";
				strLog += std::to_string(nReturnValue);
				OutputDebugString(strLog.c_str());
				return false;
			}

			//读取文件
			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);
					printf("读取文件失败");
					break;
				}
				else if (nReadFileSize == 0)           //读取文件完毕
				{
					unzCloseCurrentFile(unzfile);
					CloseHandle(hFile);
					printf("读取文件完毕");
					break;
				}
				else									//写入读取的内容
				{
					DWORD dWrite = 0;
					BOOL bWriteSuccessed = WriteFile(hFile, szReadBuffer, BUFFER_SIZE, &dWrite, NULL);
					if (!bWriteSuccessed)
					{
						unzCloseCurrentFile(unzfile);
						CloseHandle(hFile);
						break;
					}
				}
			}
			free(szReadBuffer);
		}
		unzGoToNextFile(unzfile);
	}

	if (pFileInfo)
	{
		delete pFileInfo;
		pFileInfo = NULL;
	}
	if (pGlobalInfo)
	{
		delete pGlobalInfo;
		pGlobalInfo = NULL;
	}

	//关闭
	if (unzfile)
	{
		unzClose(unzfile);
	}
	std::cout << "End unpacking the package... " << endl;
	return true;
}

std::string& CZlibHelper::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 CZlibHelper::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 < (int)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++)
	{
		//CreateDirectory 创建一个新目录
		//第一个参数值为文件夹名称,第二个参数值为安全属性,一般设置为NULL即可。
		//如果正确创建,返回值为1,如果没有正常创建文件夹,则返回0。
		bSuccess = CreateDirectoryA(vIter->c_str(), NULL) ? true : false;
	}
	return bSuccess;
}

用法测试

#include "stdafx.h"
#include <iostream>
#include <string>
#include "zlibHelper.h"

int _tmain(int argc, _TCHAR* argv[])
{
#if 1
	std::string dirpath = "C:\\Users\\Administrator\\Desktop\\zlib";			//源文件/文件夹
	std::string zipfileName = "C:\\Users\\Administrator\\Desktop\\zlib1.zip";	//目的压缩包
	//CZlibHelper::CreateZipfromDir(dirpath, zipfileName, "zlib2");
	CZlibHelper::CreateZipfromDir(dirpath, zipfileName, "");
#endif
	std::string srcFilePath = "C:\\Users\\Administrator\\Desktop\\zlib1.zip";
	std::string tempdir = "C:\\Users\\Administrator\\Desktop\\zlib2";
	CZlibHelper::UnzipFile(srcFilePath, tempdir);

	system("pause");
	return 0;
}

 


版权声明:本文为hallyz945原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。