zlib库可以解压zip压缩数据,但是在做标准zip文件解压的时候发现会出现问题,原因其实是因为zlib的压缩格式与标准zip的压缩格式有那么一点区别,最初这个问题让我很纠结,在经过实验后,我将标准zip压缩的文件与使用zlib库函数压缩的文件使用二进制编辑器打开时发现了它们的一点点区别,使用zlib库函数压缩后的数据与标准zip压缩后的数据相比在头部多了个0x9c78,(而且数据的尾部也稍有不同,这个我没有具体深入,因为我发现貌似影响不大的样子,目前没有遇到问题),所以在使用zlib函数解压标准zip压缩数据的时候我们可以将标准zip压缩的数据前面加上0x9c78再使用zlib库函数解压,这样就能顺利进行了
下面还有个问题,就是标准zip文件格式的文件,我们会发现一个zip文件里面会有很多的文件,那么我们如何知道一个zip文件里面有哪些具体内容呢?
标准zip文件的格式如下
压缩源文件数据区+压缩源文件目录区+压缩源文件目录结束标志
在这个数据区中每一个压缩的源文件/目录都是一条记录,记录的格式如下:
[文件头+ 文件数据 + 数据描述符]
文件头
组成 长度
文件头标记 4 bytes (0x04034b50)
解压文件所需 pkware 版本 2 bytes
全局方式位标记 2 bytes
压缩方式 2 bytes
最后修改文件时间 2 bytes
最后修改文件日期 2 bytes
CRC-32校验 4 bytes
压缩后尺寸 4 bytes
未压缩尺寸 4 bytes
文件名长度 2 bytes
扩展记录长度 2 bytes
文件名 (不定长度)
扩展字段 (不定长度)
这样,我们可以先读出文件头,然后读出文件名,再读出扩展字段(如果有的话)
之后我们再读出压缩的数据,将这个数据头部加入0x9c78后使用zlib库函数解压
这时如果我们得到的压缩后的数据为0则表示这是个文件夹的名称,我们创建一个文件夹,如果不为0我们就创建一个文件
一直重复上面的操作,一直到文件末尾
还有一点要注意,如果你得到的压缩后的数据大小与压缩前的数据大小相同的话,那么这个文件应该是没有被压缩过的
这时你只需要直接将它读出来并写入文件即可
下面还有个问题,就是标准zip文件格式的文件,我们会发现一个zip文件里面会有很多的文件,那么我们如何知道一个zip文件里面有哪些具体内容呢?
标准zip文件的格式如下
压缩源文件数据区+压缩源文件目录区+压缩源文件目录结束标志
在这个数据区中每一个压缩的源文件/目录都是一条记录,记录的格式如下:
[文件头+ 文件数据 + 数据描述符]
文件头
组成 长度
文件头标记 4 bytes (0x04034b50)
解压文件所需 pkware 版本 2 bytes
全局方式位标记 2 bytes
压缩方式 2 bytes
最后修改文件时间 2 bytes
最后修改文件日期 2 bytes
CRC-32校验 4 bytes
压缩后尺寸 4 bytes
未压缩尺寸 4 bytes
文件名长度 2 bytes
扩展记录长度 2 bytes
文件名 (不定长度)
扩展字段 (不定长度)
这样,我们可以先读出文件头,然后读出文件名,再读出扩展字段(如果有的话)
之后我们再读出压缩的数据,将这个数据头部加入0x9c78后使用zlib库函数解压
这时如果我们得到的压缩后的数据为0则表示这是个文件夹的名称,我们创建一个文件夹,如果不为0我们就创建一个文件
一直重复上面的操作,一直到文件末尾
还有一点要注意,如果你得到的压缩后的数据大小与压缩前的数据大小相同的话,那么这个文件应该是没有被压缩过的
这时你只需要直接将它读出来并写入文件即可
由于这里我只是简单地想要解压文件,所以并没有读取和处理压缩源目录区,下面上一个代码
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <zlib.h>
#include <errno.h>
struct data
{
unsigned long head;
unsigned short pk;
unsigned short q;
unsigned short type;
unsigned short time;
unsigned short date;
unsigned long crc;
unsigned long d_len;
unsigned long len;
unsigned short f_len;
unsigned short e_len;
}__attribute__((packed));
void error_quit(const char *msg)
{
perror(msg);
exit(-1);
}
void memcat(char *tmp,char *buf,unsigned long len)
{
int i=2,j=0;
while(len)
{
tmp[i]=buf[j];
++i;
++j;
--len;
}
}
int get_zip_file_num(char *zip_file)
{
int i=0;
FILE *fp;
struct data zip;
if((fp=fopen(zip_file,"rb"))==NULL)
return -1;
while(!feof(fp))
{
fread(&zip,sizeof(struct data),1,fp);
if(zip.f_len <= 0)
break;
fseek(fp,zip.f_len,SEEK_CUR);
if(zip.e_len > 0)
fseek(fp,zip.e_len,SEEK_CUR);
if(zip.d_len > 0)
fseek(fp,zip.d_len,SEEK_CUR);
++i;
}
fclose(fp);
return i;
}
void unzip(char *zip_file)
{
struct data zip;
FILE *fp,*out;
char *buf,*source,*name,*tmp;
unsigned short s=0x9c78;
unsigned long blen;
if((fp=fopen(zip_file,"rb"))==NULL)
error_quit("Open File");
while(!feof(fp))
{
fread(&zip,sizeof(struct data),1,fp);
if(zip.f_len <= 0)
break;
name=malloc(zip.f_len+1);
bzero(name,zip.f_len+1);
fread(name,zip.f_len,1,fp);
if(zip.e_len > 0)
fseek(fp,zip.e_len,SEEK_CUR);
if(zip.d_len == 0)
mkdir(name,0777);
else if(zip.d_len == zip.len)
{
if((out=fopen(name,"w"))==NULL)
error_quit("Create File");
printf("Unzip %s . . .\n",name);
buf=malloc(zip.d_len+1);
bzero(buf,zip.d_len+1);
fread(buf,zip.d_len,1,fp);
fwrite(buf,zip.d_len,1,out);
fclose(out);
free(buf);
printf("Unzip %s Successed . . .\n",name);
}
else
{
buf=malloc(zip.d_len+1);
bzero(buf,zip.d_len+1);
fread(buf,zip.d_len,1,fp);
source=malloc(zip.len+1);
bzero(source,zip.len+1);
tmp=malloc(zip.d_len+3);
bzero(tmp,zip.d_len+3);
memcpy(tmp,&s,sizeof(s));
memcat(tmp,buf,zip.d_len);
if((out=fopen(name,"w"))==NULL)
error_quit("Create File");
printf("Unzip %s . . .\n",name);
blen=compressBound(zip.len);
uncompress(source,&blen,tmp,zip.len);
fwrite(source,zip.len,1,out);
fclose(out);
free(source);
free(tmp);
free(buf);
printf("Unzip %s Successed . . .\n",name);
}
free(name);
}
fclose(fp);
}
int main(int argc,char **argv)
{
if(argc != 2)
error_quit("Argument Error!");
printf("%d files in %s\n",get_zip_file_num(argv[1]),argv[1]);
unzip(argv[1]);
return 0;
}