关于数据流压缩的原理,lz77以及huffman编码可以参考上一篇:

https://blog.csdn.net/sesiria/article/details/116835301

本篇将包含以下内容:

1. gzip格式分析

2. zlib库函数API分析

3. zlib库实战(压缩和解压文件)

gzip格式采用deflate算法来实现数据的压缩

一、deflate采用了改进版的lz77算法

即:三个字节以上重复才进行编码,否则不进行编码;即对滑动窗口进行查询的时候最短的匹配大小为3个字节。

为什么要最小匹配3个字节呢? 这是由于,在gzip中,<匹配长度,到匹配串开头的距离>对中,“匹配长度”的范围为3-258,也就是有256种可能指,需要8bit来保存。 “到匹配 串开头的距离"范围 为0~32k,需要15bit来保存。 所以一个<匹配长度,到匹配串开头的距离>对需要23位,差一位3个字节。

如果一个匹配串小于3个字节,使用<匹配长度,到匹配串开头的距离>对进行替换,不但没有压缩,反而还会增大。所以保存<匹配长度,到匹配串开头的距离>所队应的位数,决定了最小匹配长度至少要为3个字节。

deflate无损压缩解压算法(先lz77压缩,然后huffman编码)

deflate中的huffman编码:

对lz77得到的压缩后结果,需要统计字符生成编码表huffmantree,根据码表对内容进行编码,从而实现压缩的效果。

编码表hufmantree和编码后的data都放在一个文件中。

deflate中欧弄个的解压:

读取二进制文件,构建huffmantree表,读取数据根据huffmantree生成字符

接着用lz77解码,进行搜索匹配并替换为队应的串。

todo: 基于先使用lz77 最短3字节编码,后使用huffman编码的deflate压缩算法的实现:

二、gzip格式分析

gzip的压缩原理:先使用lz77算法的 一个变种进行压缩,对得到的结果再使用huffman编码进行压缩;

bzip2的压缩原理:使用了一个游程编码器进行编码,接下来块排序压缩和Move-to-front(MTF)变换进一步产生大量相同符号,进一步使用另一个游程编码器进行编码。最后使用huffman编码,将一个消息头与其打包;

LZMA编码:它是deflate和lz77算法改良和优化后的压缩算法,而Deflate是同时使用了lz77算法与huffman编码的一个无损 数据压缩算法。

deflate(RFC1951):一种压缩算法,使用 LZ77 和哈夫曼进行编码;
zlib(RFC1950):一种格式,是对 deflate 进行了简单的封装,他也是一个实现库(delphi
中有 zlib,zlibex);
gzip(RFC1952):一种格式,也是对 deflate 进行的封装;
https://www.rfc-editor.org/rfc/rfc1952.txt

gzip = gzip头 + deflate编码的实际内容 + gzip尾

zlib = zlib头 + deflate编码的实际内容 + zlib尾

GZIP本身只是一种文件格式,其内部通常采用Deflate数据格式,而Deflate采用lz77压缩算法来压缩数据。

GZIP文件由1~多个块组成,实际上通常只有1个块。每个块包含头,数据和尾三部分。

块的大概结构如下:

1. 头部分

ID1ID2:各1字节。固定值,ID1 = 31(0x1F), ID2 = 139(0X8B), 指示GZIP格式。

CM: 1字节。表示压缩方法。目前只有一种: CM = 8, 指Deflate算法。

FLG: 1字节,标志位。

bit 0 FTEST - 指示文本数据

bit 1 FHCRC - 指示存在CRC16头校验字段

bit 2 FEXTRA - 指示存在可选字段

bit 3 FNAME - 指示存在原文件名字段

bit 4 FCOMMENT - 指示存在注释字段

bit 5-7 保留

MTIME: 4字节。更改时间。UNIX格式。

XLF: 1字节。 附加的标志。当CM=8时, XFL = 2 表示采用最大压缩但最慢的算法;

XFL=4  表示最快但最小的压缩算法

OS: 1字节。操作系统,确切说应该是文件系统。有如下定义:

0 - FAT文件系统 (MS-DOC, OS/2, NT/Win32)

1 - Amiga

2 - VMS/OpenVMS

3 - Unix

4 - VM/CMS

5 - Atari TOS

6 - HPFS 文件xitong (OS/2, NT)

7 - Macintosh

8 - Z-System

9 - CP/M

10 - TOPS-20

11 - NTFS

12 - QDOS

13 - Acorn RISCOS

255 - 未知

额外的头字段:

若FLG.FEXTRA = 1

+---+---+---+---+===============//================+
|SI1|SI2| XLEN | 长度为 XLEN 字节的可选项 |
+---+---+---+---+===============//================+

(若 FLG.FNAME = 1)
+=======================//========================+
| 原文件名(以 NULL 结尾) |
+=======================//========================+

(若 FLG.FCOMMENT = 1)
+=======================//========================+
| 注释文字(只能使用 iso-8859-1 字符, 以 NULL 结尾) |
+=======================//========================+

(若 FLG.FHCRC = 1)
+---+---+
| CRC16 |
+---+---+

存在额外的可选项时,SI1与SI2指示可选项ID,XLEN指示可选项字节数。如SI1 = 0x41('A'), SI2 = 0x70('P') ,表示可选项是Apollo文件格式的额外数据

2. 数据部分

Deflate数据格式,包含一些列子数据块。子块结构如下:

+......+......+......+=============//============+
|BFINAL| BTYPE | 数据 |
+......+......+......+=============//============+

BFINAL: 1比特。   0 - 还有后续子快;   1- 改子快是最后一块。  BTYPE: 2比特。

00 - 不压缩;    01 - 静态Huffman编码压缩;  10 - 动态Huffman编码压缩; 11 - 保留。

3. 尾部分

CRC32: 4字节。 原始数据的32位校验和。 ISIZE: 4字节。原始(未压缩)数据长度的低32位。

GZIP中字节排列顺序是LSB方式,即Little-Endian, 与ZLIB中相反

三、zlib库API分析

1. 下载源码包

http://www.zlib.net/

选择zlib-1.2.11.tar.gz

1) 下载

wget http://www.zlib.net/zlib-1.2.11.tar.gz

2) 解压

tar -zxvf zlib-1.2.11.tar.gz

3) 进入目录

cd zlib-1.2.11

2.  编译

1)配置

./configure

2) 编译

make

3)检查,要全全部为yes

make check

4)安装

sudo make install

3. 基础数据结构( zlib.h)

z_stream_s

typedef struct z_stream_s {z_const Bytef *next_in;     /* next input byte */uInt     avail_in;  /* number of bytes available at next_in */uLong    total_in;  /* total number of input bytes read so far */Bytef    *next_out; /* next output byte will go here */uInt     avail_out; /* remaining free space at next_out */uLong    total_out; /* total number of bytes output so far */z_const char *msg;  /* last error message, NULL if no error */struct internal_state FAR *state; /* not visible by applications */alloc_func zalloc;  /* used to allocate the internal state */free_func  zfree;   /* used to free the internal state */voidpf     opaque;  /* private data object passed to zalloc and zfree */int     data_type;  /* best guess about the data type: binary or textfor deflate, or the decoding state for inflate */uLong   adler;      /* Adler-32 or CRC-32 value of the uncompressed data */uLong   reserved;   /* reserved for future use */
} z_stream;

gz_header

/*gzip header information passed to and from zlib routines.  See RFC 1952for more details on the meanings of these fields.
*/
typedef struct gz_header_s {int     text;       /* true if compressed data believed to be text */uLong   time;       /* modification time */int     xflags;     /* extra flags (not used when writing a gzip file) */int     os;         /* operating system */Bytef   *extra;     /* pointer to extra field or Z_NULL if none */uInt    extra_len;  /* extra field length (valid if extra != Z_NULL) */uInt    extra_max;  /* space at extra (only when reading header) */Bytef   *name;      /* pointer to zero-terminated file name or Z_NULL */uInt    name_max;   /* space at name (only when reading header) */Bytef   *comment;   /* pointer to zero-terminated comment or Z_NULL */uInt    comm_max;   /* space at comment (only when reading header) */int     hcrc;       /* true if there was or will be a header crc */int     done;       /* true when done reading gzip header (not usedwhen writing a gzip file) */
} gz_header;

4. 常用的函数

压缩函数

deflateInit : 参数比较少,里面的实现其实是调用deflateInit2

deflateInit2:  压缩初始化的基础函数,有很多参数。

deflate: 压缩函数

deflateEnd: 压缩完成以后,释放空间,仅仅是释放deflateInit中申请的空间,自己申请的空间还是要自己释放

compress : 全部附加选项默认压缩,内部调用compress2

compress2 : 可选择压缩level的压缩方式

压缩函数介绍

deflateInit2

int ZEXPORT deflateInit2 ((z_streamp strm, int level,int method, int windowBits,int memLevel, int strategy));

为压缩初始化内部流状态,zalloc, zfree和opaque字段必须在 调用之前初始化,如果zalloc和zfree被初始化为Z_NULL, deflateInit会更新它们而使用默认的分配函数。

压缩级别必须为Z_DEFAULT_COMPRESSION, 或者0~9之间的数;1表示最快速度的压缩,9表示最优压缩

左右压缩,0不做任何压缩,Z_DEFAULT_COMPRESSION是速度和最优压缩的折衷(一般为6)

函数成功返回Z_OK, 如果没有足够的内存则返回Z_MEM_ERROR,如果不是一个有效的压缩级别

z_stream 是这个压缩的上下文,依照官方给的例子进行初始化

strm.zalloc = NULL;
strm.zfree = NULL;
strm.opaque = NULL;
strm.next_in = //你的待压缩数据
strm.next_out = //压缩以后数据存储的 buffer
strm.avail_in = //待压缩数据的长度
strm.avail_out = //压缩数据存储 buffer 的长度

level: 压缩等级,目前有四个值

#define Z_NO_COMPRESSION 0 //不压缩
#define Z_BEST_SPEED 1 //速度优先,可以理解为最低限度的压缩.
#define Z_BEST_COMPRESSION 9 //压缩优先,但是速度会有些慢
#define Z_DEFAULT_COMPRESSION (-1) //默认选项, compress 里面用的就是这个选项

method: 值只有一个,当前唯一的deflate压缩方法,用于以后的扩展

#define Z_DEFLATED 8
/* The deflate compression method (the only one supported in this version)
*/

windowBits: 窗口比特数

-(15 ~ 8) : 纯 deflate 压缩
+(15 ~ 8) : 带 zlib 头和尾
> 16 : 带 gzip 头和尾

memLevel: 目前只有一个选项,MAX_MEM_LEVEL, 无非是运行过程中对内存使用的限制。

/* Maximum value for memLevel in deflateInit2 */
#ifndef MAX_MEM_LEVEL
# ifdef MAXSEG_64K
# define MAX_MEM_LEVEL 8
# else
# define MAX_MEM_LEVEL 9
# endif
#endif

stragegy:用于调整压缩算法,直接给默认就行Z_DEFAULT_STRATEGY

#define Z_FILTERED 1 //用于由 filter(或者称为 predictor)生成的数据
#define Z_HUFFMAN_ONLY 2 //用于强制哈夫曼编码(不做字符匹配)
#define Z_RLE 3 //限制匹配长度为 1
#define Z_FIXED 4 //阻止使用动态哈夫曼编码, 从而允许获得更简单的解码
#define Z_DEFAULT_STRATEGY 0 //用于普通数据
/* compression strategy; see deflateInit2() below for details */

deflate:压缩函数

ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));

deflate函数金可能的压缩数据,当输入缓冲为空或者输出缓冲满了则会停止,它会带来输出延迟(读入的数据没有立即输出),除非强行刷新缓冲区。

详细的语意如下,deflate会执行下面的一个或者两个动作:

1) 从next_in开始压缩输入数据从而更新next_in和avail_in. 如果不是所有输入数据都可以被处理(比如因为缓冲区没有足够的空间),next_in和avail_in会更新,当再次调用deflate()函数时输入数据会从这一点开始被处理。

2)   从next_out开始提供更多输出数据从而更新next_out和avail_out, 如果flush参数不是为0的话这个动作是强制的。经常性强制刷新缓冲区会降低压缩比例,所以只有必要时才设置这个参数。

调用deflate()函数之前, 必须至少保证(avail_in 或avail_out)被设置,用提供更多输入数据,或小号更多输出数据的方式,从而更新avail_in或avail_out; avail_out在函数被调用前千万不能为0. 应为在压缩过程中可能会随时输出压缩的数据。

当输出 缓冲区满了或者在每次调用deflate()之后,如果deflate返回Z_OK并且avail_out为0时,deflate()必须再次被调用(说明输出缓冲区还有数据应该被读取)

int flush的参数:

Z_NO_FLUSH: 通常设置为该值,允许压缩算法决定累积多少数据再产生输出,以达到压缩效率的最高。

Z_SYNC_FLUSH: 将所有等待输出的数据刷新到输出缓冲区,以字节为边界进行对齐。该刷新可能会降低压缩算法的压缩效率,它只用于必要的时候。

Z_FINISH: 如果输出和待输出的数据都被处理完,则返回Z_STREAM_END. 如果返回Z_OK 或者Z_BUF_ERROR, 则需要再次调用Z_FINISH直到返回Z_STREAM_END。

deflateEnd:资源释放

int ZEXPORT deflateEnd OF((z_streamp strm));

压缩完成后释放空间,该函数仅释放由deflateInit申请的空间,用户自己申请的需要自行释放。

解压函数介绍

inflateInit2: 解压初始化

函数原型:

int ZEXPORT inflateInit2((z_streamp strm, int windowBits);

strm, 和deflate函数中以后,初始化三个回调函数以后即可

windowBits: 函数定义和deflateInit2一样

inflate: 解压函数

int ZEXPORT inflate OF((z_streamp strm, int flush));

z_streamp : 四个参数.
strm.next_in = 你的待解压数据
strm.next_out = 解压以后数据存储的 buffer
strm.avail_in = 待解压数据的长度
strm.avail_out = 解压数据存储 buffer 的长度.
flush : 和 deflate 一样,如果是 Z_NO_FLUSH 说明还有数据没有解压,如果是 Z_FINISH 说
明这是最后一包待解压数据

inflateEnd: 资源释放

int ZEXPORT inflateEnd OF((z_streamp strm));

四,zlib实例分析

使用zlib封装库进行文件的压缩与解压

https://github.com/sesiria/Algs/tree/master/Lib/CompressLibrary

deflate 实现了使用gzip格式进行数据的压缩 deflate.cpp

inflate 实现了解压缩gzip格式

五、nginx优化之gzip压缩提升网站速度

注意:图片、视频、音频等二进制文件没必要进行压缩

配置nginx的配置文件

# 开启 gzip
gzip on;# 启用 gzip 压缩的最小文件,小于设置值的文件将不会压缩
gzip_min_length 1k;# gzip 压缩级别, 1-9,数字越大压缩的越好,也越占用 CPU 时间,后面会有详细说明
gzip_comp_level 1;# 进行压缩的文件类型。 javascript 有多种形式。其中的值可以在 mime.types 文件中找到。
gzip_types text/plain application/javascript application/x-javascript text/css
application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png
application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml;# 是否在 http header 中添加 Vary: Accept-Encoding,建议开启
gzip_vary on;# 禁用 IE 6 gzip
gzip_disable "MSIE [1-6]\.";# 设置压缩所需要的缓冲区大小
gzip_buffers 32 4k;# 设置 gzip 压缩针对的 HTTP 协议版本,没做负载的可以不用
# gzip_http_version 1.0;# 开启缓存
location ~* ^.+\.(ico|gif|jpg|jpeg|png)$ {access_log off;expires 2d;
}location ~* ^.+\.(css|js|txt|xml|swf|wav)$ {access_log off;expires 24h;
}location ~* ^.+\.(html|htm)$ {expires 1h;
}location ~* ^.+\.(eot|ttf|otf|woff|svg)$ {access_log off;expires max;
}# 格式
# expires 30s;
# expires 30m;
# expires 2h;
# expires 30d;

说明:

gzip on

打开或关闭gzip

Syntax: gzip on | off;
Default: gzip off;
Context: http, server, location, if in location

gzip_buffers

设置用于处理请求压缩的缓冲区数量和大小。比如 32 4K 表示按照内存页( one memorypage)大小以 4K 为单位(即一个系统中内存页为 K),申请 32 倍的内存空间。建议
此项不设置,使用默认值。

Syntax: gzip_buffers number size;
Default: gzip_buffers 32 4k|16 8k;
Context: http, server, location

gzip_comp_level

设置 gzip 压缩级别,级别越低压缩速度越快文件压缩比越小,反之速度越慢文件压缩比越大。

Syntax: gzip_comp_level level;
Default: gzip_comp_level 1;
Context: http, server, location

gzip_disable

通过表达式,表明哪些 UA 头不使用 gzip 压缩。

Syntax: gzip_disable regex ...;
Default: —
Context: http, server, location
This directive appeared in version 0.6.23.

gzip_min_length

正整数,单位为字节,也可用 k 表示千字节,比如写成 1024 与 1k 都可以,效果是一样的,表示当资源大于 1k 时才进行压缩,资源大小取响应头中的 Content-Length 进行比
较,经测试如果响应头不存在 Content_length 信息,该限制参数对于这个响应包是不起作用的;另外此处参数值不建议设的太小,因为设的太小,一些本来很小的文件经过压
缩后反而变大了,官网没有给出建议值,在此建议 1k 起,因为小于 1k 的也没必要压缩,并根据实际情况来调整设定。

Syntax: gzip_min_length length;
Default: gzip_min_length 20;
Context: http, server, location

gzip_http_version
用于识别 http 协议的版本,早期的浏览器不支持 gzip 压缩,用户会看到乱码,所以为了支持前期版本加了此选项。默认在 http/1.0 的协议下不开启 gzip 压缩。

Syntax: gzip_http_version 1.0 | 1.1;
Default: gzip_http_version 1.1;
Context: http, server, location

在应用服务器前,如果还有一层 Nginx 的集群作为负载均衡,在这一层上,若果没有开启 gzip。
如果我们使用了 proxy_pass 进行反向代理,那么 nginx 和后端的 upstream server之间默认是用 HTTP/1.0 协议通信的。
如果我们的 Cache Server 也是 nginx,而前端的 nginx 没有开启 gzip。
同时,我们后端的 nginx 上没有设置 gzip_http_version 为 1.0,那么 Cache 的url 将不会进行 gzip 压缩。

gzip_proxied

Nginx 做为反向代理的时候启用:

• off – 关闭所有的代理结果数据压缩
• expired – 如果 header 中包含 "Expires" 头信息,启用压缩
• no-cache – 如果 header 中包含 "Cache-Control:no-cache" 头信息,启用压缩
• no-store – 如果 header 中包含 "Cache-Control:no-store" 头信息,启用压缩
• private – 如果 header 中包含 "Cache-Control:private" 头信息,启用压缩
• no_last_modified – 启用压缩,如果 header 中包含 "Last_Modified" 头信息,
启用压缩
• no_etag – 启用压缩,如果 header 中包含 "ETag" 头信息,启用压缩
• auth – 启用压缩,如果 header 中包含 "Authorization" 头信息,启用压缩
• any – 无条件压缩所有结果数据

Syntax: gzip_proxied off | expired | no-cache | no-store | private | no_last_modified
| no_etag | auth | any ...;
Default: gzip_proxied off;
Context: http, server, location

gzip_types

设置需要压缩的 MIME 类型,如果不在设置类型范围内的请求不进行压缩。 匹配 MIME 类型进行压缩,(无论是否指定 ) "text/html"类型总是会被压缩的。

Syntax: gzip_types mime-type ...;
Default: gzip_types text/html;
Context: http, server, location

gzip_vary

增加响应头 "Vary: Accept-Encoding"告诉接收方发送的数据经过了压缩处理,开启后的效果是在响应头部添加了 AcceptEncoding:gzip,这对于本身不支持 gzip 压缩的客户端浏览器有用。

Syntax: gzip_vary on | off;
Default: gzip_vary off;
Context: http, server, location

数据流压缩之应用篇zlib库相关推荐

  1. C++ ZIP压缩文件,使用zlib库和codeproject上的zip_utils

    PS; zlib库比较老了,好像是Version 1.01e.基本功能也差不多. part1: 使用codeproject上的完整的库,大牛弄的是真的方便简单. 地址:http://www.codep ...

  2. 使用zlib库实现gzip格式压缩

    在产品设备上没有gzip压缩命令,但有zlib库,可以根据zlib库很方便的实现gzip压缩.下载zlib的源码,在test目录下面有几个测试程序,根据这几个测试程序基本可以写出想要的程序了.如下是一 ...

  3. 使用zlib库解压*.zip文件

    1.编辑zlib静态库 1)实现zip文件压缩和解压的zlib库部分代码(基于zlib-1.2.5) 这是zlib-1.2.5源码:http://download.csdn.net/download/ ...

  4. QT4.8.6调用zlib库实现数据流的压缩与解压缩

    转载sunnysab :https://blog.csdn.net/sunnysab/article/details/46672949 ZLIB库 ZLIB版本zlib 1.2.11  地址:http ...

  5. 字符编码、Unicode原理、数据流压缩Zlib与Miniz的实现

    字符集和字符编码的区别和联系 字符集:多个字符的集合.例如 GB2312 是中国国家标准的简体中文字符集,GB2312 收录简化汉字(6763 个)及一般符号.序号.数字.拉丁字母.日文假名.希腊字母 ...

  6. python中zlib库用法详解(压缩与解压缩)

    zlib主要用于压缩与解压缩 字符串:使用zlib.compress可以压缩字符串.使用zlib.decompress可以解压字符串. 数据流:压缩:compressobj,解压:decompress ...

  7. C++zlib库实现的GZIP压缩解压缩工具类【多图警告^_^】

    前几天和服务端同事联调TCP传输压缩后的Json串,遇到了 GZIP压缩问题. 需要把原来使用的的压缩算法和后端统一,现在来总结一下. [注意] 这里就以Windows的Debug环境为例,Relea ...

  8. 数据流压缩原理实现(huffman编码,LZ77压缩算法)

    1. 压缩原理deflate算法 a) LZ77 算法原理 b) Huffman算法原理 c) Huffman算法测试实例 2.  关于zlib库的实际应用以及gzip格式分析查看下一篇 一.数据压缩 ...

  9. 理解和使用zlib库 - 我个人的救赎

    理解和使用zlib库 作者: 阙荣文 日期: 2016.6.2 0. 很多年以前我曾经写过一篇文章(http://blog.csdn.net/querw/article/details/1452041 ...

最新文章

  1. mysql定义外键_MySQL定义外键的方法
  2. python画正方形-用Python Turtle画一个正方形
  3. EF-DbUpdateException解决方案
  4. CentOS6.8 搭建SVN并用钩子自动实现同步到web目录
  5. STM32外设之GPIO的推挽输出和开漏输出模式详解
  6. 计算机术语中CPU是指______,试题题库_计算机基础知识考试试卷全套.doc
  7. boost::geometry模块变换多边形的测试程序
  8. php input 数组的值,如何获取2值表单输入数组到PHP数组中(How to get 2 value form input array into PHP array)...
  9. android enum 内存,Android 中的 Enum 到底占多少内存?该如何用?
  10. 安装 java decompiler_Eclipse离线安装Java Decompiler插件(反编译)
  11. js写随机输出7位数代码教程
  12. php ajax练习
  13. 数学建模常用模型22:回归模型
  14. 荧光染料BDP FL maleimide/马来酰亚胺,CAS:773859-49-7
  15. linux显示分区命令,在Linux中查看分区表的4种方法
  16. 硬盘安装FC6 linux
  17. 数据分析画图:50道练习玩转matplotlib
  18. 什么是FE/GE/XG端口???
  19. 数字IC后端实现TOP Floorplan专家秘籍
  20. v-model的使用

热门文章

  1. iOS RGB 颜色对照表
  2. 周报速递丨企业微信 4.0 发布;零售、餐饮 10 大业态数字化盘点
  3. python控制视频播放器的大小与位置_python_十几行代码实现简单播放器
  4. 总结一下购买阿里云服务器的经验
  5. javascript学习_真正学习javascript
  6. 没有SOA,CIO 将面临什么
  7. 在线快速将pdf转换成word
  8. 亚像元定位 硬分类 软分类
  9. 事件抽取与事理图谱(二)
  10. C# windows服务:创建Windows服务(Windows Services)的一般步骤