使用ZLIB库压缩与解压缩

背景


ZLIB是一套免费、通用、无损的数据压缩库,可以在任何硬件及操作系统上使用,而且ZLIB数据格 式可以跨平台移植。ZLIB提供了一套in-memory压缩和解压函数,并能检测解压出来的数据的完整性,ZLIB也支持读写gzip(.gz)格式的文件.

步骤


  • ZLIB官网下载ZLIB库的源码
  • 打开\contrib\vstudio\vc[对应的编译环境版本,选择最接近自己环境的即可]\zlibvc.sln项目
  • 编译zlibstat静态库、生成zlibstat.lib
  • 新建项目,复制ZLIB官网下回来的zlib.hzconf.h和编译好的zlibstat.lib到项目目录

编译环境配置


  • Debug编译模式下的编译设置
    首先,打开项目工程的属性页,展开“C/C++”,单击“预处理器”,在“预处理器定义”中添加ZLIB_WINAPI,否则,代码不能 编译通过。
    接着,单击“代码生成”,在“运行库”中设置为“/MTd”选项,表示在Debug模式下的多线程静态编译。
    最后,展开“链接器”,单击“命令行”,在“其他选项(D)”编辑框中添加链接命 令“/FORCE:MULTIPLE”,这个选项使链接器创建一个有效的exe文件或dll文件,即使一个函数或变量多次 引用或多处定义。
  • Release编译模式下的编译设置
    和Debug模式一样,首先打开项目工程的属性页,展开“C/C++”,单击“预处理器”,在“预处理器定义”中添加ZLIB_WINAPI,否则,代码不能 编译通过。
    接着,单击“代码生成”,在“运行库”中设置为“/MT”选项,表示在Release模式下的多线程静态编译。
    最后,展开“链接器”,单击“命令行”,在“其他选项(D)”编辑框中添加链接命令“/SAFESEH:NO”, 这样就解决了“SAFESEH 映像不安全”的问题。
  • 注意,使用zlib-1.2.11时存在一个问题,编译zlibstat.lib库的时候需要将Realize更改为ReleaseWithoutAsm,否则在实际调用zlib解压函数时可能会导致异常

测试代码


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
/************************************main.h  头文件*************************************/
#include <windows.h>
#include<stdio.h>
#include<shlwapi.h>
#include "zconf.h"
#include "zlib.h"

#pragma comment(lib,"zlibstat.lib")
#pragma comment(lib,"shlwapi.lib")


#define MAX_SRC_FILE_SIZE (500*1024*1024)//500M

void Zlib_ShowError(char *pszText)
{
char szErr[MAX_PATH] = {0};
sprintf(szErr,"%s Error[%d]\n",pszText,GetLastError());
MessageBox(NULL,szErr,"ERROR",MB_OK | MB_ICONERROR);

}
// 数据压缩
// 输入:将要压缩文件的路径
// 输出:数据压缩后的压缩数据内容、数据压缩后的压缩数据内容长度
BOOL Zlib_CompressData(char *pszCompressFileName,BYTE ** ppCompressData,DWORD *pdwCompressDataSize)
{
//*************压缩文件********************//
HANDLE hFile = CreateFile(pszCompressFileName,GENERIC_READ,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
Zlib_ShowError("CreateFile");
return FALSE;
}

DWORD dwFileSize = GetFileSize(hFile, NULL); // 获取文件大小
if (MAX_SRC_FILE_SIZE < dwFileSize)
{
CloseHandle(hFile);
return FALSE;
}

// 判断是否满足大小限制条件
if (MAX_SRC_FILE_SIZE < dwFileSize)
{
CloseHandle(hFile);
return FALSE;
}
DWORD dwDestDataSize = dwFileSize;

BYTE *pSrcData = new BYTE[dwFileSize];
if (NULL == pSrcData)
{
CloseHandle(hFile);
return FALSE;
}
BYTE *pDestData = new BYTE[dwDestDataSize];
if (NULL == pDestData)
{
CloseHandle(hFile);
return FALSE;
}

DWORD dwRet = 0;
ReadFile(hFile, pSrcData, dwFileSize, &dwRet, NULL); // 读取文件数据
if ((0 >= dwRet) ||
(dwRet != dwFileSize))
{
delete[]pDestData;
pDestData = NULL;
delete[]pSrcData;
pSrcData = NULL;
CloseHandle(hFile);
return FALSE;
}

// 压缩数据
/*
int compress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);
compress函数将source缓冲区中的内容压缩到dest缓冲区。sourceLen表示source缓冲区的大小(以字节计)。
注意:函数的第二个参数destLen是传址调用,当调用函数时,destLen表示dest缓冲区大小(初始值不能为0哦),
( destLen > (sourceLen + 12) * 100.1% );当函数退出后,destLen表示压缩后缓冲区的实际大小。
此时,destLen/sourceLen正好是压缩率!!!
返回值:
-5 : 输出缓冲区不够大;
-4 : 没有足够的内存;
0 : 表示成功;
*/
int iRet = 0;
do
{
iRet = compress(pDestData, &dwDestDataSize, pSrcData, dwFileSize);
if (0 == iRet)
{
// 成功
break;
}
else if (-5 == iRet)
{
// 输出缓冲区不够大, 以 100KB 大小递增
delete[]pDestData;
pDestData = NULL;
dwDestDataSize = dwDestDataSize + (100 * 1024);
pDestData = new BYTE[dwDestDataSize];
if (NULL == pDestData)
{
delete[]pSrcData;
pSrcData = NULL;
CloseHandle(hFile);
return FALSE;
}
}
else
{
// 没有足够的内存 或 其他情况
delete[]pDestData;
pDestData = NULL;
delete[]pSrcData;
pSrcData = NULL;
CloseHandle(hFile);
return FALSE;
}
} while (TRUE);
// 返回数据
*ppCompressData = pDestData;
*pdwCompressDataSize = dwDestDataSize;

// 释放
// delete[]pDestData;
// pDestData = NULL;
delete[]pSrcData;
pSrcData = NULL;
CloseHandle(hFile);

return TRUE;
}
BOOL SaveToFile(char *pszFileName,BYTE *pData,DWORD dwDataSize)
{
HANDLE hFile = CreateFile(pszFileName,GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_ARCHIVE,NULL);
DWORD dwRet = 0;
WriteFile(hFile,pData,dwDataSize,&dwRet,NULL);
CloseHandle(hFile);

return TRUE;
}

// 数据解压
// 输入:将要解压缩文件的路径
// 输出:数据解压后的数据内容、数据解压后的内容长度
BOOL Zlib_UncompressData(char *pszUncompressFileName, BYTE **ppUncompressData, DWORD *pdwUncompressDataSize)
{
// 注意可能出现压缩后的文件比压缩前的文件大的现象!!!

// 打开文件 并 获取文件数据
HANDLE hFile = CreateFile(pszUncompressFileName, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_ARCHIVE, NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
Zlib_ShowError("CreateFile");
return FALSE;
}

DWORD dwFileSize = GetFileSize(hFile, NULL); // 获取文件大小
DWORD dwDestDataSize = MAX_SRC_FILE_SIZE;

BYTE *pSrcData = new BYTE[dwFileSize];
if (NULL == pSrcData)
{
CloseHandle(hFile);
return FALSE;
}
BYTE *pDestData = new BYTE[dwDestDataSize];
if (NULL == pDestData)
{
CloseHandle(hFile);
return FALSE;
}

DWORD dwRet = 0;
ReadFile(hFile, pSrcData, dwFileSize, &dwRet, NULL); // 读取文件数据
if ((0 >= dwRet) ||(dwRet != dwFileSize))
{
delete[]pDestData;
pDestData = NULL;
delete[]pSrcData;
pSrcData = NULL;
CloseHandle(hFile);
return FALSE;
}

// 解压缩数据
/*
int uncompress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);
compress函数将source缓冲区中的内容压缩到dest缓冲区。sourceLen表示source缓冲区的大小(以字节计)。
注意:函数的第二个参数destLen是传址调用,当调用函数时,destLen表示dest缓冲区大小(初始值不能为0哦),
( destLen > (sourceLen + 12) * 100.1% );当函数退出后,destLen表示压缩后缓冲区的实际大小。
此时,destLen/sourceLen正好是压缩率!!!
返回值:
-5 : 输出缓冲区不够大;
-4 : 没有足够的内存;
0 : 表示成功;
*/
int iRet = 0;
do
{
iRet = uncompress(pDestData, &dwDestDataSize, pSrcData, dwFileSize);
if (0 == iRet)
{
// 成功
break;
}
else if (-5 == iRet)
{
// 输出缓冲区不够大, 以 100KB 大小递增
delete[]pDestData;
pDestData = NULL;
dwDestDataSize = dwDestDataSize + (100 * 1024);
pDestData = new BYTE[dwDestDataSize];
if (NULL == pDestData)
{
delete[]pSrcData;
pSrcData = NULL;
CloseHandle(hFile);
return FALSE;
}
}
else
{
// 没有足够的内存 或 其他情况
delete[]pDestData;
pDestData = NULL;
delete[]pSrcData;
pSrcData = NULL;
CloseHandle(hFile);
return FALSE;
}
} while (TRUE);
// 返回数据
*ppUncompressData = pDestData;
*pdwUncompressDataSize = dwDestDataSize;

// 释放
// delete[]pDestData;
// pDestData = NULL;
delete[]pSrcData;
pSrcData = NULL;
CloseHandle(hFile);

return TRUE;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/************************************	main.cpp  *************************************/
#include "main.h"

int main(int argc,char *argv[],char *envp[])
{
BOOL bRet = FALSE;
BYTE *pCompressData = NULL;
DWORD dwCompressDataSize = 0;

BYTE *pUncompressData = NULL;
DWORD dwUncompressDataSize = 0;

bRet = Zlib_CompressData(argv[1],&pCompressData,&dwCompressDataSize);
//BOOL Zlib_CompressData(char *pszCompressFileName,BYTE ** ppCompressData,DWORD *pdwCompressDataSize)
if(bRet == FALSE)
{
Zlib_ShowError("Zlib_CompressData fail!");
delete [] pCompressData;
pCompressData = NULL;
return 0;
}

SaveToFile("yeanhoo.myzip",pCompressData,dwCompressDataSize);
bRet = Zlib_UncompressData("yeanhoo.myzip",&pUncompressData,&dwUncompressDataSize);
//BOOL Zlib_UncompressData(char *pszUncompressFileName, BYTE **ppUncompressData, DWORD *pdwUncompressDataSize)
if(bRet == FALSE)
{
Zlib_ShowError("Zlib_UncompressData fail!");
delete [] pUncompressData;
pUncompressData = NULL;
delete [] pCompressData;
pCompressData = NULL;
return 0;
}
SaveToFile("yeanhoo.exe",pUncompressData,dwUncompressDataSize);
delete [] pUncompressData;
pUncompressData = NULL;
delete [] pCompressData;
pCompressData = NULL;

getchar();
return 0;
}

项目用到的相关代码点击下载