在运维与 Web 开发中,我经常会听到这三个名词:编译 Nginx 时需要添加 zlib 库,启用 ngx_gzip_static_module,PHP 中增加 zip 模块可以用来处理 ZIP 文件,Web 服务器启用 Gzip 压缩可以节省传输带宽和时间,HTTP/1.1 协议中又有 Content-Encoding: gzip 和 Content-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,也就不再是三个容易混淆的名词了。
评论0
欢迎分享你的看法,也欢迎补充不同的实践经验。