「Node.js」白露欲霜,聊聊zlib压缩

简介: 今天分享Node.js中zlib压缩的相关知识。

前言

完成对Node.js的从了解到熟练的进阶这个Flag设立已久,久到去年就有它了。白露欲霜,隔年的Flag是时候拿出来实现了。躺平or码字,我决定选择后者。

至少对Node.js的探索,今年能有一个完美的叹号。

目标明确

秋日懒洋洋,特别适合窗前看书。光影斑驳,再来杯白开水。最近看zlib压缩的API,发现无论从理解还是使用上都比较陌生,所以挑了一些看着感兴趣的API进行进一步的摸索。

随波逐流无归处,乘风破浪济沧海

zlib 压缩

瞧一瞧,一个压缩/解压功能包含了多少知识点?

文件压缩和解压的实现

letzlib=require('zlib');
const { createReadStream, createWriteStream } =require('fs');
const { pipeline } =require('stream');
/** * 压缩过程中错误捕获方法 */constonError=err=> {
if (err) {
console.error('An error occurred:', err);
process.exitCode=1;
  }
};
/** * 压缩或者解压方法 type值为zip执行压缩方法,type值为ungzip执行解压缩方法 */functionzipFunc(source, destination, type) {
constgzip=zlib.createGzip();
constungzip=zlib.createGunzip();
switch (type) {
case'zip':
returnpipeline(source, gzip, destination, onError);
case'ungzip':
// 或者用pipeline方法,return pipeline(source, ungzip, destination, onError);returnsource.pipe(ungzip).pipe(destination);
default:
returnpipeline(source, gzip, destination, onError);
  }
}
// 压缩constsource=createReadStream('./zlib/input.txt');
constdestination=createWriteStream('./zlib/input.txt.gz');
zipFunc(source, destination, 'zip');
// 解压constsource=createReadStream('./zlib/input.txt.gz');
constdestination=createWriteStream('./zlib/input.txt');
zipFunc(source, destination, 'ungzip');
  • 执行压缩操作时,zlib目录下生成input.txt.gz文件;
  • 执行解压操作时,zlib目录下生成input.txt文件;


pipeline

stream.pipeline()方法,用于在流和生成器之间进行管道转发错误并正确清理并在管道完成时提供回调。

难以理解的功能介绍?

上面这段功能介绍,我看了好多遍, 并没有理解,尤其是对于管道的概念比较模糊。于是搜了一下stream的文章,发现了一篇好文Node.js 中的一股清流:理解 Stream(流)的基本概念》,写的很详细易懂,它里有这样一段话:

管道是一种机制,是将一个流的输出作为另一流的输入。它通常用于从一个流中获取数据并将该流的输出传递到另外的流。管道操作没有限制,换句话说,管道用于分步骤处理流数据。

所以在进行文件压缩的时候使用stream.pipeline()提供一个完成数据流处理的管道,管道内可以传输多个流,管道任务结束后提供回调。

用法

stream.pipeline(source[, ...transforms], destination, callback)

属性

source:可读流

...tranforms:双工流(同时实现 Readable 和 Writable 接口的流

destination:可写流

callback:管道完成时的回调


pipe

readable.pipe() 方法将可写流绑定到可读流,使其自动切换到流动模式并将其所有数据推送到绑定的可写流。 将这句话总结一下,pipe方法的主要用途是从可读流中读取数据写入可写流。

用法

readable.pipe(destination[, options])

示例

可以看官方的示例,简单易懂,将 readable 中的所有数据通过管道传输到名为 file.txt 的文件中:

constfs=require('fs');
constreadable=getReadableStreamSomehow();
constwritable=fs.createWriteStream('file.txt');
// 可读流的所有数据进入 'file.txt'。readable.pipe(writable);
也可以将多个Writable流绑定到单个Readable流。readable.pipe() 方法返回对目标流的引用,从而可以建立管道流链constfs=require('fs');
constr=fs.createReadStream('file.txt');
constz=zlib.createGzip();
constw=fs.createWriteStream('file.txt.gz');
r.pipe(z).pipe(w);

stream 流

什么是流?

看下官网的介绍。

流是用于在 Node.js 中处理流数据的抽象接口。 stream 模块提供了用于实现流接口的 API。

流可以是可读的、可写的、或两者兼而有之。 所有的流都是 EventEmitter 的实例。

我看完,好像懂了又好像没懂。但是我找到了一篇讲的非常好的文章,一文搞定 Node.js 流 (Stream)

这篇文章里面对流的介绍,我感觉懂了一些

stream(流)是一种抽象的数据结构。就像数组或字符串一样,流是数据的集合。

不同的是,流可以每次输出少量数据,而且它不用存在内存中。

比如,对服务器发起 http 请求的 request/response 对象就是 Stream。

总结一下,使用流可以将文件资源拆分成小块进行处理,减轻服务器压力。

明白了流的作用,就知道为什么文件压缩要使用Stream提供的模块方法了。如果想对Stream进行更深入的了解,推荐阅读《一文搞定 Node.js 流 (Stream)》,写的详情且通俗易懂。

压缩 HTTP 的请求和响应

gzip、deflate 和 br

  • gzip是一种数据格式,默认且目前仅使用deflate算法压缩data部分;
  • deflate是同时使用了LZ77算法与哈夫曼编码(Huffman Coding)的一个无损数据压缩算法。
  • Brotli 通过变种的 LZ77 算法、Huffman 编码以及二阶文本建模等方式进行数据压缩,与其他压缩算法相比,它有着更高的压缩效率。


官网示例的本地实验

我再官网给出的示例的基础上,将http的响应内容生成不同的文件,可以看出压缩过和未经过压缩的文件的文件大小是有区别的。

示例代码

// 客户端请求示例constzlib=require('zlib');
consthttp=require('http');
constfs=require('fs');
const { pipeline } =require('stream');
constrequest=http.get({
host: 'example.com',
path: '/',
port: 80,
headers: { 'Accept-Encoding': 'br,gzip,deflate' },
});
request.on('response', response=> {
console.log(response.headers['content-encoding'], 'headers');
constoutput=fs.createWriteStream('./zlib/example.com_index_default.html');
// const output = fs.createWriteStream('./zlib/example.com_index_gzip.html');// const output = fs.createWriteStream('./zlib/example.com_index_deflate.html');constonError=err=> {
if (err) {
console.error('An error occurred:', err);
process.exitCode=1;
    }
  };
switch (response.headers['content-encoding']) {
case'br':
pipeline(response, zlib.createBrotliDecompress(), output, onError);
break;
case'gzip':
pipeline(response, zlib.createGzip(), output, onError);
break;
case'deflate':
pipeline(response, zlib.createGzip(), output, onError);
break;
default:
pipeline(response, output, onError);
break;
  }
});
  • 未经过压缩的文件大小是1.2k;
  • 压缩过的文件大小是600多B;

小结

对http请求和响应的压缩,我还有待在实际应用场景中研究和实践,单纯实现官网的例子,我感觉自己没有完全掌握。

缓存压缩


总结

在过去一个月的学习中,虽然都是碎片化的学习,但是随着技术的积累,形成一套属于自己的学习体系,可以帮助更快更好的掌握新技术。

接下来,就是实践的阶段了,虽然工作中没有使用Node.js开发的场景,但是自己可以创造项目,正好我有一个现成的小程序,可以开发一套文章管理后台系统。

康肃问曰:“汝亦知射乎?吾射不亦精乎?”翁曰:“无他,但手熟尔。”康肃忿然曰:“尔安敢轻吾射!”翁曰:“以我酌油知之。”乃取一葫芦置于地,以钱覆其口,徐以杓酌油沥之,自钱孔入,而钱不湿。因曰:“我亦无他,惟手熟尔。



目录
相关文章
|
5月前
|
前端开发 JavaScript 安全
从前端性能优化角度谈JavaScript代码压缩与混淆
本文从前端性能优化的角度出发,探讨了JavaScript代码压缩与混淆的重要性及实现方式,通过分析不同压缩混淆工具的特点和效果,为开发者提供了实用的指导和建议。
|
JSON JavaScript 数据格式
NPM 发包 js 文件并支持 ts 使用(包含 gulp 打包压缩)
NPM 发包 js 文件并支持 ts 使用(包含 gulp 打包压缩)
143 0
|
5月前
|
JSON JavaScript 数据格式
NPM 发包 js 文件并支持 ts 使用(包含 gulp 打包压缩)
NPM 发包 js 文件并支持 ts 使用(包含 gulp 打包压缩)
113 0
|
28天前
|
缓存 JavaScript 中间件
优化Express.js应用程序性能:缓存策略、请求压缩和路由匹配
在开发Express.js应用时,采用合理的缓存策略、请求压缩及优化路由匹配可大幅提升性能。本文介绍如何利用`express.static`实现缓存、`compression`中间件压缩响应数据,并通过精确匹配、模块化路由及参数化路由提高路由处理效率,从而打造高效应用。
78 6
|
2月前
|
JavaScript 前端开发 安全
JS 混淆解析:JS 压缩混淆原理、OB 混淆特性、OB 混淆JS、混淆突破实战
JS 混淆解析:JS 压缩混淆原理、OB 混淆特性、OB 混淆JS、混淆突破实战
51 2
|
4月前
|
自然语言处理 JavaScript 前端开发
JS代码是如何被压缩的
JS代码是如何被压缩的
28 1
|
4月前
|
移动开发 JavaScript 前端开发
使用GruntJS链接与压缩多个JavaScript文件
使用GruntJS链接与压缩多个JavaScript文件
30 3
|
前端开发 JavaScript Java
前端——使用RequireJS的r.js打包压缩模块
前端——使用RequireJS的r.js打包压缩模块
|
JavaScript 前端开发
Gulp 打包压缩 js 文件到指定目录详细流程(修改文件名与后缀)
Gulp 打包压缩 js 文件到指定目录详细流程(修改文件名与后缀)
194 0
|
5月前
|
存储 JavaScript 算法
Nodejs 第二十四章(zlib)
Nodejs 第二十四章(zlib)
59 0