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 ...
随机推荐
- Clover软件使用中遇到的问题
安装Clover应用后不生效,Win + E 时仍然默认打开系统的资源管理器 解决: 进入ie浏览器的管理加载项窗口,显示栏选择所有加载项 找到 Explorer Watcher Class 项,确保 ...
- 使用Eureka中遇到的一些问题
1.情况 : 服务已经注册到eureka,eureka中可以看到,但是 consumer和provider这两个服务,一直报错, 错误信息:DiscoveryClient_CONSUMER-DEM ...
- Java String类相关知识梳理(含字符串常量池(String Pool)知识)
目录 1. String类是什么 1.1 定义 1.2 类结构 1.3 所在的包 2. String类的底层数据结构 3. 关于 intern() 方法(重点) 3.1 作用 3.2 字符串常量池(S ...
- 深入理解协程(二):yield from实现异步协程
原创不易,转载请联系作者 深入理解协程分为三部分进行讲解: 协程的引入 yield from实现异步协程 async/await实现异步协程 本篇为深入理解协程系列文章的第二篇. yield from ...
- 使用iview遇到问题记录总结
1.iview设置日期不可用,设置开始开始时间早于结束时间 官网示例,设置今天之前不可选,但是不能识别thisdisabledDate (date) { return date && ...
- 弹性碰撞 poj 3684
Simon is doing a physics experiment with N identical balls with the same radius of R centimeters. Be ...
- 异数OS TCP协议栈测试(二)--短连接篇
异数OS TCP协议栈测试(二)--短连接篇 本文来自异数OS社区 github: 异数OS-织梦师(消息中间件)群: 476260389 测试目标 TCP 短链接IO性能测试,Client Se ...
- 为BlueLake主题增加自定义icon图标
一.前言 hexo 的 Bluelake 主题是我一直在用的,简单大方,很喜欢.但最近有了添加自定义 icon 图标的需求,比如,添加 "地址"."扫一扫".& ...
- .net core webapi搭建(1)
创建一个webapi项目 修改launchSettings.json 将launchSettings.json中的IIS启动删掉.别问我为啥 原因就是IISEXPRESS有时候需要我手动重启.我嫌麻 ...
- tensorboard网络结构
一.tensorboard网络结构 import tensorflow as tffrom tensorflow.examples.tutorials.mnist import input_data ...