ZIP文件是一种很常见的压缩文件格式,用户在windows下经常要使用WINZIP程序进行文件压缩和解压操作。不过,WINZIP程序只能由用户操作,而没有提供开发方面的接口。这样,要想在用户的应用程序中加入文件压缩和解压功能,就有一定困难了。幸好,有ZLIB这个开放源代码的压缩和解压库可供开发人员使用。不过,ZLIB虽然支持文件压缩和解压,但只能对Linux/Unix下的GZ文件进行读写操作,对于Windows系统下的ZIP文件并不提供直接的支持。要解决ZLIB不能直接操作Windows ZIP文件的矛盾,就需要对Windows下ZIP文件结构进行分析,再结合ZLIB所提供的相关函数,加上一些开发技巧,自行开发相应的Winzip程序。
一、ZIP文件结构
ZIP文件基本结构如下:
{分文件头信息+文件压缩数据}+中心目录+中心目录记录结束符
更详细的说明如下:
每个分文件头信息后面紧跟此文件压缩数据。如果压缩方式是不压缩,就是该文件的从第1个字节一直到最后一个字节的原始字节流;如果压缩方式是deflate,压缩数据就是经过deflate算法压缩过的字节流。
ZIP文件中通常有若干个分文件数据,最多可达到65535个。
文件的最后修改时间和日期按MS-DOS时间日期格式编码。时间和日期均为16位整数。
对于时间,16位格式分配如下:
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
小时 | 分 | 秒 |
对于日期,16位格式分配如下:
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
年-1980 | 月 | 日 |
ZIP文件使用32位CRC校验码检查数据是否有误。
ZIP文件中单个文件和ZIP文件本身字节数不能大于4G,否则会出错。
文件名实际长度由文件名长这个字段指定,文件名中可以包含路径,但不包含驱动器盘符(要特别注意的是,所有路径分割符’\’都要转换为’/’,并且路径中第一个字符不能是’/’)。
外部文件属性这个字段,最低1个字节是文件的DOS属性字节,其它字节均设为0)。其中DOS属性字节格式如下:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
未用 | 未用 | 档案 | 目录 | 卷标 | 系统 | 隐藏 | 只读 |
分文件头相对位移和中心目录初始偏移都是按字节表示的相对于ZIP文件开始位置的偏移量,用于在ZIP文件中准确定位。
中心目录中每个文件都可以有注释,用户可以将需要保存的额外信息保存在该字段中。
二、ZLIB使用说明
ZLIB的作者是文件压缩方面的专家,通过一系列复杂的算法实现了deflate这种格式的压缩与解压,并且为开发人员提供了相对简单的函数接口。
ZLIB流格式在RFC1950中有详细的定义。对于ZLIB流,除去流中开头的2个字节和结尾的4个字节,中间的连续字节流即经过deflate转换的压缩流。
ZLIB是开放源代码的,可以从www.zlib.net上下载源代码包,也可以下载编译好的Windows系统下的DLL。目前最新的ZLIB版本为1.2.3,dll文件名为zlib1.dll,头文件名为zlib.h,还需要用一个zconf.h。
本程序一共使用了ZLIB提供的6个函数,其中压缩函数3个,解压函数3个。
压缩要用到deflateInit、deflate、deflateEnd这3个函数,解压要用到inflateInit,inflate,inflateEnd这3个函数。
deflateInit在压缩开始之间调用,压缩结束后调用deflateEnd;同样,解压之前调用inflateInit,解压完成后调用inflateEnd。这4个函数都很好理解。
关键函数是deflate进行压缩,inflate进行解压。
deflate函数有两个参数,stream和flush。stream是一个结构体变量,有next_in、avail_in、next_out、avail_out这四个变量。next_in表示当前输入的字节数组,avail_in表示当前可用的输入字节数;next_out表示当前输出的字节数组,avail_out表示当前可用的输出字节数。当输入数据没有结束时flush设为Z_NO_FLUSH,否则设为Z_FINISH。next_out要至少比next_in大0.0015%。
inflate函数参数和deflate相同,但flush总是设为Z_NO_FLUSH。
为压缩整个文件,应当循环调用deflate函数进行数据压缩。同样,也要循环调用inflate函数进行解压。当avail_out这个变量为0时,表示输出缓冲已满,这时需要将next_out中数据写入文件,写入字节数由next_out大小-avail_out决定。然后重新调用deflate进行压缩或inflate进行解压。
三、程序开发过程
本程序使用Visual C++ 6.0开发。使用MFC AppWizard生成基于对话框的应用程序框架。
主对话框截图如图1所示。
图1 主界面
主对话框中加入菜单,包括文件和动作两个子菜单,其中文件菜单中只有一个打开菜单项,用于打开ZIP文件。动作菜单有加入、删除、解出三个菜单项。
菜单下面是一个MSFlexGrid控件,用于显示ZIP文件中的文件。
主对话框底部有三个标签,用于显示有关信息。
用户单击文件菜单的打开命令,会出现打开文件对话框,用于打开或新建ZIP文件。
用户单击动作菜单中的加入命令,会出现选择文件对话框,由用户选择要加入的文件。
选择文件对话框截图如2所示。
图2选择文件对话框
该对话框中,压缩和保存路径两个复选按钮默认都是选中状态,即进行压缩和保存文件路径,用户可以改变这个设置。压缩复选按钮左边是一个下拉列表框,由用户选择驱动器,上面的MSFlexGrid列表用于显示当前目录和文件,用户在该列表按空格键选择文件,选中文件将出现在对话框下面的列表框中,单击确定按钮则进行将选择文件加入ZIP文件。
用户单击动作菜单的解压按钮,会出现解压对话框,由用户输入解压路径。
解出对话框截图如下:
用户只要输入一个解压目录,并单击确定按钮,就会将ZIP文件中文件解压至该目录。
源文件中,有4个包含开发时加入的源代码:winzip.cpp中定义一些全局变量;selectfiledlg.cpp为选择文件对话框类;extractdlg.cpp为输入解压路径对话框类;winzipdlg.cpp为主对话框类,其中包含主要的源代码;其它文件均由MFC自动生成。2