你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:杂志经典 / 计算机安全与维护
6.5 使用ZLIB开发的WINZIP文件管理器
 

ZIP文件是一种很常见的压缩文件格式,用户在windows下经常要使用WINZIP程序进行文件压缩和解压操作。不过,WINZIP程序只能由用户操作,而没有提供开发方面的接口。这样,要想在用户的应用程序中加入文件压缩和解压功能,就有一定困难了。幸好,有ZLIB这个开放源代码的压缩和解压库可供开发人员使用。不过,ZLIB虽然支持文件压缩和解压,但只能对Linux/Unix下的GZ文件进行读写操作,对于Windows系统下的ZIP文件并不提供直接的支持。要解决ZLIB不能直接操作Windows ZIP文件的矛盾,就需要对WindowsZIP文件结构进行分析,再结合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文件使用32CRC校验码检查数据是否有误。

    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.3dll文件名为zlib1.dll,头文件名为zlib.h,还需要用一个zconf.h

本程序一共使用了ZLIB提供的6个函数,其中压缩函数3个,解压函数3个。

压缩要用到deflateInitdeflatedeflateEnd3个函数,解压要用到inflateInitinflateinflateEnd3个函数。

deflateInit在压缩开始之间调用,压缩结束后调用deflateEnd;同样,解压之前调用inflateInit,解压完成后调用inflateEnd。这4个函数都很好理解。

关键函数是deflate进行压缩,inflate进行解压。

deflate函数有两个参数,streamflushstream是一个结构体变量,有next_inavail_innext_outavail_out这四个变量。next_in表示当前输入的字节数组,avail_in表示当前可用的输入字节数;next_out表示当前输出的字节数组,avail_out表示当前可用的输出字节数。当输入数据没有结束时flush设为Z_NO_FLUSH,否则设为Z_FINISHnext_out要至少比next_in0.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自动生成。

winzip.cpp中定义的全局变量如下:

CWinzipApp *theApp=new CWinzipApp;

 //应用程序指针,AppWizard产生的是一个变量,要将其改成指针,否则编译会报错

CMenu *menu1=new CMenu;  //用户制作的菜单

CString *zipfilepath=new CString;  //用于标识带路径的zip文件名

unsigned long color1,color2,color3;  //color1表示表格前景颜色;color2表示表格背景颜色;color3表示选中表格背景颜色

unsigned short int compressmode=0x08;  //用于标识压缩方法 0x08-deflate压缩;  0x00-不压缩

unsigned short int savepath=1;  //用于标识是否保存路径 1-保存路径 0-不保存路径

CString *statustext=new CString;  //显示在提示栏中的文字

CString *offsetstr=new CString;  //用于存放zip文件中文件的偏移地址

unsigned short int selectcount;  //标识选中文件个数

CString *extractpath=new CString;  //标识解压文件路径

CString *packsizestr=new CString;  //标识解压文件大小

CString *filenamestr=new CString;  //标识解压文件名称

CString *defaultpath=new CString;  //标识默认zip文件所在路径

CString *addfilenamestr=new CString;  //标识要加入文件名称

int addflag=0;  //加入标志 0-不加入;1-加入

 

selectfiledlg.cppCdialog派生一个对话框类,用于压缩文件时选择要加入的文件。

selectfiledlg.cpp中有以下几个函数:

1void FindAllFiles(CString dirname,CListBox &list);  //查找dirname目录下及其子目录中所有文件,结果放在一列表控件中

2int deletefiles(CString zipfile);  //ZIP文件中删除文件

3UINT do_zipfiles(LPVOID pParam);  /*压缩文件线程,实际先从zip文件中删除相同文件*/

4BOOL Cselectfiledlg::OnInitDialog(); /*初始化文件选择对话框*/

5void Cselectfiledlg::OnSelchangeCombo1(); /*用户选择驱动器后更新该驱动器下目录*/

6void Cselectfiledlg::OnDblClickMsflexgrid1(); /*用户双击目录列表进入相应目录,并显示目录下文件和子目录*/

7void Cselectfiledlg::OnKeyDownMsflexgrid1(short FAR* KeyCode, short Shift);  /*用户使用空格键选中目录列表中文件,选中文件放入最下面列表框中*/

8void Cselectfiledlg::OnLButtonDown(UINT nFlags, CPoint point);  /*按鼠标左键则取消所有表格控件中选中的文件*/

9void Cselectfiledlg::OnButton1(); /*用户单击确定按钮开始进行压缩*/

extractdlg.cpp中有以下几个函数:

1UINT do_unzipfiles(LPVOID pParam);  /*定义解压文件线程*/

2void Cextractdlg::OnButton1(); /*用户按确定按钮进行解压操作*/

winzipdlg.cpp为主对话框源文件,其中包括了本程序最核心部分的代码,下面进行详细描述:

程序中加入菜单,资源IDIDR_MENU1,包括文件和操作两个子菜单,其中文件菜单中只有一个打开菜单项,用于打开ZIP文件。操作菜单有加入、删除、解出三个菜单项。

在主对话框的OnInitDialog事件中加入以下两行代码就可正常使用菜单了。

menu1->LoadMenu(IDR_MENU1);

SetMenu(menu1);

Winzipdlg.cpp中有如下函数:

1void lowerstr(char *str); /*将输入字符串转换为小写*/

2void FindAllFiles(CString dirname,CListBox &list); /*dirname中找文件,结果放入list中,通过递归查找子目录中文件*/

3int convdrive(char *drive); /*将驱动器字母转换为数字*/

4unsigned long Reflect(unsigned long int ref, char ch); /*crc内部用函数*/

5void init_crc32_table(void); //初始化crc32

6unsigned long GenerateCRC32(char * DataBuf,unsigned long  len); /*DataBuf中长度为len的串取CRC*/

7unsigned long FileGenerateCRC32(CString filename); /*filename文件取CRC*/

8unsigned short int istextfile(CString filename);/*判断filename类型

返回值:1-textfile 0-binaryfile -1-无此文件*/

9int makedir(char *drive,char *path); /*drive驱动器和path目录下建立目录,path开头和结尾都要有'\'*/

10int deletefiles(CString zipfile); /*zipfile中删除选中文件*/

11int zipfiles(CString zipfile); /*ZIP文件中加入文件*/

12int unzipfiles(CString zipfile); /*ZIP文件中解压文件*/

13UINT do_zipfiles1(LPVOID pParam); /*加入文件线程*/

14BOOL CWinzipDlg::OnInitDialog();/*初始化主对框,并进行CRC表初始化*/

15void CWinzipDlg::OnClose(); /*关闭主对话框*/

16void CWinzipDlg::OnSize(UINT nType, int cx, int cy); /*当用户改变窗口大小时相应改变空间大小*/

17void CWinzipDlg::OnOpen(); /*用户打开zip文件*/

18void CWinzipDlg::OnExtract(); /*用户从zip文件解出文件*/

19UINT do_deletefiles(LPVOID pParam); /*zip文件删除文件线程*/

20void CWinzipDlg::OnDelete(); /*用户从zip文件删除文件*/

21void CWinzipDlg::OnAdd(); /*用户向zip文件加入文件*/

22BOOL CWinzipDlg::PreTranslateMessage(MSG* pMsg); /*处理用户定义消息*/

23void CWinzipDlg::OnKeyDownMsflexgrid1(short FAR* KeyCode, short Shift); /*用户按空格键选中或取消ZIP文件列表中文件*/

24void CWinzipDlg::OnLButtonDown(UINT nFlags, CPoint point); /*按鼠标左键取消表格控件选中文件*/

主对话框中加入MSFlexGrid X控件,用于显示ZIP文件目录。

程序要使用zlib1.dll这个动态链接库,将deflateInitdeflatedeflateEndinflateInitinflateinflateEnd6个函数引用到用户程序后使用。

为避免压缩文件数量众多或文件太大时应用程序失去用户响应,使用工作线程进行压缩、解压、删除操作,通过全局变量传递所用参数,使用消息发送机制输出执行结果。

选择文件时使用递归查找目录下子目录中所有文件;其它还包括CRC码生成;判定文件是否文本文件;在指定路径下循环建立子目录等相关函数;具体代码可参见源程序。

使用6个字节的数组保存ZLIB中开头2个字节和结尾4个字节,这6个字节保存在中心目录的注释字段中。只要能准确地保存和读取ZLIB流中这6个字节,就能够完成ZIP文件压缩和解压功能。

在弄清楚ZIP文件结构基础上,可以较容易地编写实现压缩、解压、删除、和查看这4ZIP文件中最基本的功能的源代码。下面分别进行详述:

因代码太长,不便全部列出,故只对实现的步骤进行描述,具体代码可参见源程序。

压缩部分操作步骤为:

1)将zlib1.dll3个压缩函数引入

2)打开ZIP文件fp1用于读

3)打开一个不带扩展名的ZIP文件fp2用于读写

4)从fp1中取中心目录初始偏移

5)将fp1中开始至中心目录前数据全部拷入fp2中(这部分是zip文件中原来的分文件数据)

6)将要加入文件逐个写入fp2中(首先写入分文件头信息,然后是分文件头数据,如果压缩方式是不压缩,则直接写入,如果压缩方式是deflate压缩,则调用deflate函数进行压缩后写入)

7)将原ZIP文件中心目录写入fp2

8)逐个将新加入文件中心目录写入fp2(注意如压缩方式为deflate,则要向该文件注释中写入zlib流中头两个字节和末尾4个字节,以便于以后解压缩)

9)更新fp2的中心目录记录结束符

10)关闭fp1,fp2,并将fp2改名为原ZIP文件

解压操作步骤为:

1)将zlib1.dll3个解压函数引入

2)建立解压目录

3)打开ZIP文件fp1用于读

4)逐个从ZIP文件中心目录中取要解出文件名称,偏移量等信息,将该文件从ZIP文件中提取出来,并写到解压路径下(如压缩方式为不压缩,则直接读取并写入,如压缩方式为deflate,,则调用inflate函数解压后再写入,该文件注释中有zlib流中开始2个字节和末尾两个字节,结合文件注释将zlib流拼完整就可顺利解压,文件写完后还要设置文件属性和进行CRC校验)

5)关闭fp1

删除部分操作步骤为:

1)打开ZIP文件fp1用于读

2)打开不带扩展名的ZIP文件fp2用于写

3)逐个查找fp1中心目录,如果发现该文件不是要删除的文件,则将文件fp1指针移到该分文件偏移处,将该分文件数据写入fp2,如果发现该文件是要删除的文件,则继续查找fp1中心目录的下一条记录

4)更新fp2中心目录

5)更新fp2中心目录记录结束符

6)关闭fp1fp2,将fp2改名为原ZIP文件

查看ZIP文件目录操作步骤为:

1)打开ZIP文件fp1

2)找到中心目录,并逐个读取文件目录信息(包括文件名、修改时间、原始大小、压缩后大小、文件属性、文件路径、CRC校验码、相对偏移)。并将读出信息写到主对话框MSFlexGrid控件列表中

3)关闭fp1

四、结语

经过以上一系列工作,本程序开发成功。本程序可以生成符合ZIP文件结构规范的压缩文件,也可顺利地从ZIP文件中解压文件、从ZIP文件中删除文件、查看ZIP文件目录。程序已通过大量严格测试,验证了在各种可能的输入下输出结果的正确性。

  推荐精品文章

·2024年9月目录 
·2024年8月目录 
·2024年7月目录 
·2024年6月目录 
·2024年5月目录 
·2024年4月目录 
·2024年3月目录 
·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录
·2023年10月目录
·2023年9月目录 
·2023年8月目录 

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089