数字营销 · Web开发 · 基础设施

zlib gzip与zip

从 Web 开发和运维中经常遇到的 zlib、gzip、zip 出发,分析 Deflate 压缩算法、zlib 数据格式、Gzip 格式以及 ZIP 归档格式之间的关系。

在运维与 Web 开发中,我经常会听到这三个名词:编译 Nginx 时需要添加 zlib 库,启用 ngx_gzip_static_module,PHP 中增加 zip 模块可以用来处理 ZIP 文件,Web 服务器启用 Gzip 压缩可以节省传输带宽和时间,HTTP/1.1 协议中又有 Content-Encoding: gzipContent-Encoding: deflate……

那么,这三个东西到底是什么,又有什么关系?我想很多人都跟我一样,容易被搞糊涂。

先来看一下 zlib。

zlib 的官方网站是 www.zlib.net,它是一个免费的数据压缩库。zlib 最核心的压缩算法是 Deflate,同时提供对应的解压功能。

由于代码是开源的,我们不妨把源码下载下来编译一下。编译之后,在 zlib 的安装目录中可以看到下面这些文件:

[root@server zlib]# tree
.
├── include
│ ├── zconf.h
│ └── zlib.h
├── lib
│ ├── libz.a
│ ├── libz.so -> libz.so.1.2.11
│ ├── libz.so.1 -> libz.so.1.2.11
│ ├── libz.so.1.2.11
│ └── pkgconfig
│ └── zlib.pc
└── share
└── man
└── man3
└── zlib.3

6 directories, 8 files

仔细观察会发现,zlib 并没有像普通应用程序一样生成一个可以直接执行的程序,而主要提供头文件、库文件和文档。

也就是说,zlib 本身是一个,并不是一个专门用来操作磁盘文件的压缩程序。

再来看 zlib 的文档,其中对它的描述是一个通用的数据压缩库,提供内存中的压缩和解压功能。zlib 的核心就是 Deflate 压缩算法以及对应的解压功能。

zlib 提供了一些比较简单的函数来压缩和解压数据:

int ZEXPORT compress2(
dest,
destLen,
source,
sourceLen,
level
);

int ZEXPORT compress(
dest,
destLen,
source,
sourceLen
)
{
return compress2(
dest,
destLen,
source,
sourceLen,
Z_DEFAULT_COMPRESSION
);
}

通过参数可以看到,zlib 的压缩函数并没有直接操作文件,它接收的是内存中的数据,因此可以理解为:

内存数据

zlib

压缩后的内存数据

至于最终是否保存到文件,则由调用 zlib 的程序自己决定。

我们来测试一下使用 compress() 压缩数据:

#include <stdio.h>
#include <string.h>
#include <zlib.h>
#include <stdlib.h>

int my_write(char* fname, const char* buffer, size_t len);

int main(int argc, char** argv)
{
if(argc == 1){
printf("Please Input A String You want to Compress\n");
return -1;
}

char *str = argv[1];

printf(
"The String You Want to Compress is: %s\n",
str
);

// 需要压缩的数据长度
uLong sLen = strlen(str);

// 计算压缩缓冲区所需大小
uLong tLen = compressBound(sLen);

// 分配压缩数据空间
Bytef *cspace = malloc(tLen);

if(cspace == NULL){
printf("Not enough memory!\n");
return -1;
}

// 开始压缩
int result = compress(
cspace,
&tLen,
(const Bytef*)str,
sLen
);

if(result == Z_OK){
printf("Compress Success!\n");

int j = my_write(
"compressdata.bin",
(const char *)cspace,
tLen
);

if(j == 0){
printf("\t-Success to write into disk!\n");
}else{
printf("\t-Failure to write into disk!\n");
}
}

free(cspace);

return 0;
}

int my_write(
char* fname,
const char* buffer,
size_t len
)
{
FILE *pFile = fopen(fname, "wb");

if(pFile == NULL){
return 1;
}

size_t writesize = fwrite(
buffer,
1,
len,
pFile
);

fclose(pFile);

return writesize == len ? 0 : 1;
}

编译运行:

[root@server ~]# gcc zcompress.c -I /tmp/zlib/include/ -lz -L /tmp/zlib/lib/ -o z
[root@server ~]# ./z "This is the string"

The String You Want to Compress is: This is the string
Compress Success!
-Success to write into disk!

到这里一切正常。那么,zlib 提供的就是 Deflate 算法本身吗?严格来说还不是。

zlib 除了提供压缩算法实现之外,还定义了一种 zlib 数据格式。它是在 Deflate 压缩数据的基础上增加了一定的头部和尾部信息。所以我们实际上需要区分:

Deflate

压缩算法 / 压缩数据

zlib format

zlib头部 + Deflate数据 + zlib尾部

HTTP/1.1 中 Content-Encoding: deflate 所表示的并不是裸的 Deflate 数据,而是按照 zlib 格式封装的数据。具体格式可以参考 RFC 1950

那么,Gzip 和 zlib 又有什么区别呢?Gzip 同样使用 Deflate 作为主要压缩算法,但它采用的是另一种数据封装格式。Gzip 文件包含自己的头部信息、压缩数据以及校验等信息,其中压缩方法字段 8 表示 Deflate。

简单来说:

Deflate
├── zlib format
└── gzip format

也就是说,zlib 和 Gzip 并不是两个不同的压缩算法。它们都可以使用 Deflate,只是外围的数据封装格式不同。

那么 ZIP 又是什么呢?

ZIP 同样可以使用 Deflate,但它本质上是一个归档格式,可以在一个文件中保存多个文件以及目录结构。因此,ZIP 和 Gzip 的一个非常明显的区别就是:

gzip
→ 更偏向于压缩一个数据流 / 文件

zip
→ 更偏向于把多个文件组织起来,并进行归档和压缩

ZIP 并不意味着只能使用 Deflate,它本身支持多种压缩方法;Deflate 只是其中最常见的一种。所以,我们可以重新整理一下三者的关系。

zlib、gzip与zip的关系

1,zlib

zlib 是一个压缩库,主要实现 Deflate/Inflate,并提供 zlib 数据格式以及相应的 API。

2,gzip

Gzip 是一种文件/数据流格式及相关工具,通常使用 Deflate 进行压缩,并带有自己的头部、尾部和校验信息。

3,zip

ZIP 是一种归档格式,可以把多个文件和目录组织到一个文件中,并对其中的数据进行压缩。Deflate 是 ZIP 中常见的一种压缩方式。

4,HTTP 中的 gzip 和 deflate

HTTP 的:

Content-Encoding: gzip

表示响应主体使用 Gzip 格式进行编码。而:

Content-Encoding: deflate

在 HTTP 标准语境中表示使用 zlib 格式封装的 Deflate 数据,并不是单纯的裸 Deflate 数据。

所以最终可以简单理解成:

Deflate
/ \
/ \
zlib format gzip format
↑ ↑
zlib库 gzip工具/格式

ZIP

归档 + 压缩容器

这样一来,平时在 Nginx、PHP、Web 服务器以及 Linux 运维中经常碰到的 zlib、Gzip、ZIP,也就不再是三个容易混淆的名词了。

参考资料

  1. Zlib
  2. zlib FAQ

评论0

欢迎分享你的看法,也欢迎补充不同的实践经验。