Node.js实战--资源压缩与zlib模块

Blog:《NodeJS模块研究 - zlib》
nodejs 的 zlib 模块提供了资源压缩功能。例如在 http 传输过程中常用的 gzip,能大幅度减少网络传输流量,提高速度。本文将从下面几个方面介绍 zlib 模块和相关知识点:
文件压缩 / 解压
HTTP 中的压缩/解压
压缩算法:RLE
压缩算法:哈夫曼树
文件的压缩/解压
以 gzip 压缩为例,压缩代码如下:
const zlib = require("zlib");
const fs = require("fs");
const gzip = zlib.createGzip();const rs = fs.createReadStream("./db.json");
const ws = fs.createWriteStream("./db.json.gz");
rs.pipe(gzip).pipe(ws);
如下图所示,4.7Mb 大小的文件被压缩到了 575Kb。

解压刚才压缩后的文件,代码如下:
const zlib = require("zlib");
const fs = require("fs");
const gunzip = zlib.createGunzip();const rs = fs.createReadStream("./db.json.gz");
const ws = fs.createWriteStream("./db.json");
rs.pipe(gunzip).pipe(ws);
HTTP 中的压缩/解压
在服务器中和客户端的传输过程中,浏览器(客户端)通过 Accept-Encoding 消息头来告诉服务端接受的压缩编码,服务器通过 Content-Encoding 消息头来告诉浏览器(客户端)实际用于编码的算法。
服务器代码示例如下:
const zlib = require("zlib");
const fs = require("fs");
const http = require("http");
const server = http.createServer((req, res) => {
const rs = fs.createReadStream("./index.html");
// 防止缓存错乱
res.setHeader("Vary", "Accept-Encoding");
// 获取客户端支持的编码
let acceptEncoding = req.headers["accept-encoding"];
if (!acceptEncoding) {
acceptEncoding = "";
}
// 匹配支持的压缩格式
if (/\bdeflate\b/.test(acceptEncoding)) {
res.writeHead(200, { "Content-Encoding": "deflate" });
rs.pipe(zlib.createDeflate()).pipe(res);
} else if (/\bgzip\b/.test(acceptEncoding)) {
res.writeHead(200, { "Content-Encoding": "gzip" });
rs.pipe(zlib.createGzip()).pipe(res);
} else if (/\bbr\b/.test(acceptEncoding)) {
res.writeHead(200, { "Content-Encoding": "br" });
rs.pipe(zlib.createBrotliCompress()).pipe(res);
} else {
res.writeHead(200, {});
rs.pipe(res);
}
});server.listen(4000);
客户端代码就很简单了,识别 Accept-Encoding 字段,并进行解压:
const zlib = require("zlib");
const http = require("http");
const fs = require("fs");
const request = http.get({
host: "localhost",
path: "/index.html",
port: 4000,
headers: { "Accept-Encoding": "br,gzip,deflate" }
});
request.on("response", response => {
const output = fs.createWriteStream("example.com_index.html");
<span class="hljs-keyword" style="color: #333; font-weight: bold; line-height: 26px;">switch</span> (response.headers[<span class="hljs-string" style="color: #d14; line-height: 26px;">"content-encoding"</span>]) {
<span class="hljs-keyword" style="color: #333; font-weight: bold; line-height: 26px;">case</span> <span class="hljs-string" style="color: #d14; line-height: 26px;">"br"</span>:
response.pipe(zlib.createBrotliDecompress()).pipe(output);
<span class="hljs-keyword" style="color: #333; font-weight: bold; line-height: 26px;">break</span>;
<span class="hljs-comment" style="color: #998; font-style: italic; line-height: 26px;">// 或者, 只是使用 zlib.createUnzip() 方法去处理这两种情况:</span>
<span class="hljs-keyword" style="color: #333; font-weight: bold; line-height: 26px;">case</span> <span class="hljs-string" style="color: #d14; line-height: 26px;">"gzip"</span>:
response.pipe(zlib.createGunzip()).pipe(output);
<span class="hljs-keyword" style="color: #333; font-weight: bold; line-height: 26px;">break</span>;
<span class="hljs-keyword" style="color: #333; font-weight: bold; line-height: 26px;">case</span> <span class="hljs-string" style="color: #d14; line-height: 26px;">"deflate"</span>:
response.pipe(zlib.createInflate()).pipe(output);
<span class="hljs-keyword" style="color: #333; font-weight: bold; line-height: 26px;">break</span>;
<span class="hljs-keyword" style="color: #333; font-weight: bold; line-height: 26px;">default</span>:
response.pipe(output);
<span class="hljs-keyword" style="color: #333; font-weight: bold; line-height: 26px;">break</span>;
}
});
从上面的例子可以看出来,3 种对应的解压/压缩 API:
zlib.createInflate()和zlib.createDeflate()
zlib.createGunzip()和zlib.createGzip()
zlib.createBrotliDecompress()和zlib.createBrotliCompress()
压缩算法:RLE
RLE 全称是 Run Length Encoding, 行程长度编码,也称为游程编码。它的原理是:记录连续重复数据的出现次数。它的公式是:字符 * 出现次数。
例如原数据是 AAAAACCPPPPPPPPERRPPP,一共 18 个字节。按照 RLE 的规则,压缩后的结果是:A5C2P8E1R2P3,一共 12 个字节。压缩比例是:12 / 17 = 70.6%
RLE 的优点是压缩和解压非常快,针对连续出现的多个字符的数据压缩率更高。但对于ABCDE类似的数据,压缩后数据会更大。
压缩算法:哈夫曼树
哈夫曼树的原理是:出现频率越高的字符,用尽量更少的编码来表示。按照这个原理,以数据ABBCCCDDDD为例:
| 字符 | 编码(二进制) |
|---|---|
| D | 0 |
| C | 1 |
| B | 10 |
| A | 11 |
原来的数据是 10 个字节。那么编码后的数据是:1110101110000,一共 13bit,在计算机中需要 2 个字节来存储。这样的压缩率是:2 / 10 = 20%。
但是仅仅按照这个原理编码后的数据,无法正确还原。以前 4bit 为例,1110可以理解成:
11 + 10
1 + 1 + 1 + 0
1 + 1 + 10
...
而哈夫曼树的设计就很巧妙,能正确还原。哈夫曼树的构造过程如下:

无论哪种数据类型(文本文件、图像文件、EXE 文件),都可以采用哈夫曼树进行压缩。
参考链接

扫码关注「心谭博客」,查看「前端图谱」&「算法题解」,坚持分享,共同成长

Node.js实战--资源压缩与zlib模块的更多相关文章
- Node基础:资源压缩之zlib
概览 做过web性能优化的同学,对性能优化大杀器gzip应该不陌生.浏览器向服务器发起资源请求,比如下载一个js文件,服务器先对资源进行压缩,再返回给浏览器,以此节省流量,加快访问速度. 浏览器通过H ...
- iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 处理静态资源
视频地址:https://www.cctalk.com/v/15114923882788 处理静态资源 无非花开花落,静静. 指定静态资源目录 这里我们使用第三方中间件: koa-static 安装并 ...
- iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 错误处理
沪江CCtalk视频地址:https://www.cctalk.com/v/15114923887518 处理错误请求 爱能遮掩一切过错. 当我们在访问一个站点的时候,如果访问的地址不存在(404), ...
- iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 记录日志
沪江CCtalk视频地址:https://www.cctalk.com/v/15114923883523 log 日志中间件 最困难的事情就是认识自己. 在一个真实的项目中,开发只是整个投入的一小部分 ...
- iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 视图Nunjucks
视频地址:https://www.cctalk.com/v/15114923888328 视图 Nunjucks 彩虹是上帝和人类立的约,上帝不会再用洪水灭人. 客户端和服务端之间相互通信,传递的数据 ...
- iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 解析JSON
视频地址:https://www.cctalk.com/v/15114923886141 JSON 数据 我颠倒了整个世界,只为摆正你的倒影. 前面的文章中,我们已经完成了项目中常见的问题,比如 路由 ...
- iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 代码分层
视频地址:https://www.cctalk.com/v/15114923889408 文章 在前面几节中,我们已经实现了项目中的几个常见操作:启动服务器.路由中间件.Get 和 Post 形式的请 ...
- 《Node.js实战(双色)》作者之一——吴中骅访谈录
- iKcamp团队制作|基于Koa2搭建Node.js实战项目教学(含视频)☞ 环境准备
安装搭建项目的开发环境 视频地址:https://www.cctalk.com/v/15114357764004 文章 Koa 起手 - 环境准备 由于 koa2 已经开始使用 async/await ...
随机推荐
- WTM 3.1发布,完美支持.netcore 3.1
在过去的2019年,承蒙各位的厚爱,WTM从零开始一年的时间在GitHub上收获了将近1600星,nuget上的下载量累计超过10万. WTM所坚持的低码开发,快速实现的理念受到了越来越多.netco ...
- Ubuntu 18.04 + ROS Melodic + TurtleBot3仿真
1. 下载安装包 官网地址: http://wiki.ros.org/action/show/Robots/TurtleBot?action=show&redirect=TurtleBot 所 ...
- 快速部署postfix邮件服务器
• 装包.配置.起服务– 默认的标准配置即可为本机提供发/收邮件服务– 若有必要,可扩大服务范围(邮件域) 前提:邮件服务器,必须为手工配置永久主机名虚拟机server0[root@server0 ~ ...
- IOS系统唤醒微信内置地图
针对前一篇文章 唤醒微信内置地图 后来发现在IOS系统中运行 唤醒地图会无效的问题.因为在IOS上无法解析这俩个字符串的问题! 需要对经纬度 使用 “parseFloat()”进行转换 返回一个浮点数 ...
- MySQL插入操作
说明:value的值可以为数据,DEFAULT,NULL,expr 含有ATUO_INCREMENT的列可以插入DEFAULT.NULL,或者不插入记录来实现自动增长. 插入记录的三种方法:①可以同时 ...
- python 矩阵向右旋转90°(分行输入输出)
输入格式 第一行输入两个整数n,m,用空格隔开. 接下来n行,每行输入m个整数,表示输入矩阵.矩阵元素都是int范围内的整数. 输出格式 输出m行,每行n个空格隔开的整数,表示旋转以后的矩阵.注意:每 ...
- git利用hooks实现自动部署
准备工作: 1.一台虚拟linux环境和window 开始工作 1.安装git(略) 2.创建git用户和创建test.git裸仓库 [root@localhost ~]# useradd -m gi ...
- 写了个 Task.WhenAll(t)的一个例子。
public static void Main() { var t = Task.Run(() => { throw new Exception("aa"); }); Tas ...
- 重拾c++第三天(6):分支语句与逻辑运算符
1.逻辑运算符 && || ! 2.关系运算符优先级高于逻辑运算符 3.cctype库中好用的判断 4. ?:符号用法: 状态1?结果1:结果2 5.switch用法: switch ...
- 防止过拟合的方法 预测鸾凤花(sklearn)
1. 防止过拟合的方法有哪些? 过拟合(overfitting)是指在模型参数拟合过程中的问题,由于训练数据包含抽样误差,训练时,复杂的模型将抽样误差也考虑在内,将抽样误差也进行了很好的拟合. 产生过 ...