Nodejs 第二十四章 zlib
Node.js中的zlib模块提供了通过Gzip和Deflate/Inflate压缩和解压数据的能力。这个模块对于处理大量数据非常重要,尤其是在需要优化网络传输效率和减少数据存储空间时。
- zlib,这个名称是直接用来描述该库的功能------即提供压缩(zlib compression)功能的库
为什么需要zlib模块?
- 性能优化:压缩数据可以显著减少在网络上传输的数据量,提高响应速度,减少带宽使用。
- 成本节约:在存储大量数据时,压缩数据可以减少所需的存储空间,从而降低存储成本。
- 数据完整性 :zlib还支持校验功能,可以在数据解压缩时验证数据的完整性,确保数据在传输或存储过程中未被损坏。
zlib模块解决的难点
- 数据处理效率:没有压缩,处理大量数据(尤其是通过网络传输)可能会非常低效和成本高昂,而服务器的资源是有限的。所以前端在上传内容到服务器或OSS上的时候,都会变成一个压缩包再上传
- 透明性 :zlib模块提供了一种透明的方法来处理压缩和解压,开发者无需深入了解压缩算法的内部实现细节。
- 兼容性:支持广泛使用的Gzip和Deflate压缩算法,确保与多种应用和服务的兼容性。
主要功能
- 
压缩和解压: - zlib.createGzip():创建一个Gzip压缩流,用于压缩数据。
- zlib.createGunzip():创建一个Gzip解压流,用于解压数据。
- zlib.createDeflate():创建一个Deflate压缩流。
- zlib.createInflate():创建一个Inflate解压流。
 
- 
同步和异步操作: - zlib.gzip(),- zlib.gunzip():异步压缩/解压数据。
- zlib.gzipSync(),- zlib.gunzipSync():同步压缩/解压数据,当在处理非常大的数据集时,异步方法更适合使用,以避免阻塞事件循环。
 
- 
实用函数: - zlib.deflateRaw()、- zlib.inflateRaw()等:提供更多底层的压缩解压功能,允许更精细控制压缩解压过程。
 
通过使用这些功能,开发者可以很方便地在Node.js应用中实现数据的压缩和解压,以优化性能和资源使用。zlib模块因其强大的功能和简便
两种常用压缩方式
gzip压缩
- 让我们来简要实现一遍压缩,其实三行代码就能实现最核心的功能
            
            
              js
              
              
            
          
           const zlib = require("node:zlib")
 const fs = require("node:fs")
 
 const readStream = fs.createReadStream('天才俱乐部.txt')
 const writeStream = fs.createWriteStream('天才俱乐部.txt.gz')
 readStream.pipe(zlib.createGzip()).pipe(writeStream)

- 
通过这个效果,我们能够明显的看到,压缩效率也是非常可以啊 
- 
那在这三行代码中,其实有两行代码已经是我们非常熟悉的了,在fs模块中已经有所学习过。分别是创建可读流和创建可写流。 - 我们给txt内容创建了一个可读流,把内容转化成buffer形式
- 然后对可读流的buffer进行压缩处理。这也就是第三行,涉及到了一个pipe的管道方法,我们还没有学到,后面会进行讲解
- 处理完的buffer流导入到可写流生成了txt.gz压缩文件内
 
pipe又是什么?
在 Node.js 中,.pipe() 方法用于将一个流(stream)的输出连接到另一个流的输入。这是一种通过管道(pipe)连接流操作的方法,非常类似于 Unix 中的管道,允许数据从一个地方流向另一个地方。在 readStream.pipe(zlib.createGzip()).pipe(writeStream) 的上下文中,.pipe() 方法起着关键作用
.pipe() 方法的作用
- 数据传输简化 :.pipe()方法自动管理数据的流动。当读取流(readStream)有数据可读时,它会自动将这些数据写入到写入流(writeStream)中,无需手动监听data事件并写入数据。
- 背压管理 :当使用 .pipe()方法时,如果写入流无法处理速度过快的数据(例如写入硬盘速度跟不上数据输入速度),它能自动暂停或减慢读取流的数据读取速率,直到写入流再次准备好接受更多数据。这个过程称为背压(backpressure)管理。
为什么使用 .pipe()?
使用 .pipe() 可以非常高效地处理大量数据,如文件操作、网络通信等,而无需将所有数据一次性加载到内存中。这样做可以显著降低内存消耗,提高程序性能,特别是处理大型文件或数据流时。
对于 Buffer 的意义
在 Node.js 中,Buffer 对象用于以字节序列的形式表示二进制数据。使用 .pipe() 方法处理流时,数据通常会被分割成小块 Buffer 传输。这种方式可以持续地处理数据,每次只处理一小块,而不是一次性加载整个数据集进内存,从而更有效地利用系统资源。
- 不知道大家有没有注意到平时去商场时,看见的自动扶梯。在没有人坐自动扶梯的时候,他的传送带会以很慢或者干脆停止的速度运行,而当有人踩上去准备去上一层或下一层的时候,他的速度就会开始加快。
- 这种能够根据具体接收情况来调节传输速度的就跟pipe是差不多的。放扶梯上能有效节约电源,放pipe管道到明显可见的就是降低内存消耗,提高性能了
gzip解压缩
- 
和压缩案例一样,解压缩也是能够三行代码解决,其中两行可读流和可写流的创建是重复的 - createGunzip和createGzip就分别为创建解压缩程序和创建压缩程序的意思,也分别是zlib对应的两个方法
 
            
            
              js
              
              
            
          
           const zlib = require("node:zlib")
 const fs = require("node:fs")
 
 //首先我们拿到压缩包的数据,将其转化为可读流
 const readStream = fs.createReadStream('我的诡异人生模拟器.txt.gz')
 //创建一个可写流,也就是:我的诡异人生模拟器-xiaoyu.txt这一个文件,但目前是空的
 const writeStream = fs.createWriteStream(':我的诡异人生模拟器-xiaoyu.txt')
 //对压缩包可读流进行zlib库的createGunzip解压缩处理,然后放入可写流当中。这两步骤豆通过管道完成
 readStream.pipe(zlib.createGunzip()).pipe(writeStream)
- 根据压缩前和压缩后的文件对比,内容也是一行都没少

deflate压缩
- 
和gzip的方式也是大同小异,方法前缀一样是 create,就只是将Gzip替换为deflate- 使用方法一样,让我们的学习难度也是降低了很多
 
            
            
              js
              
              
            
          
           const readStream = fs.createReadStream('我的诡异人生模拟器.txt'); // 创建可读流,读取名为 index.txt 的文件
 const writeStream = fs.createWriteStream('我的诡异人生模拟器.txt.deflate'); // 创建可写流,将压缩后的数据写入 index.txt.deflate 文件
 readStream.pipe(zlib.createDeflate()).pipe(writeStream)

- 通过文件的大小对比,也能够明显的看到文件大小从接近14MB,降低到了5.5MB的程度
deflate解压缩
- 
使用方式一样,将可读流进行处理的方法从 createDeflate()中,修改为createInflate()即可- 而Deflate是缩小的意思,Inflate则是膨胀的意思,这也使他们对应了压缩与解压缩的含义
 
            
            
              js
              
              
            
          
           const readStream = fs.createReadStream('我的诡异人生模拟器.txt.deflate')
 const writeStream = fs.createWriteStream('我的诡异人生模拟器-xiaoyu.txt')
 readStream.pipe(zlib.createInflate()).pipe(writeStream)
gzip 和 deflate 区别
gzip(GNU zip)是一种广泛使用的压缩格式,不仅包括了deflate压缩算法,还有一些额外的头信息。这些信息包括原始文件名、文件大小和时间戳等。gzip设计初衷是用于Unix系统中文件的压缩
deflate是一个压缩算法,也是gzip的核心算法。它结合了LZ77算法和Huffman编码,提供了一种平衡了速度与压缩效率的解决方案
- 压缩算法:Gzip 使用的是 Deflate 压缩算法,该算法结合了 LZ77 算法和哈夫曼编码。LZ77 算法用于数据的重复字符串的替换和引用,而哈夫曼编码用于进一步压缩数据。
- 压缩效率:Gzip 压缩通常具有更高的压缩率,因为它使用了哈夫曼编码来进一步压缩数据。哈夫曼编码根据字符的出现频率,将较常见的字符用较短的编码表示,从而减小数据的大小。
- 压缩速度:相比于仅使用 Deflate 的方式,Gzip 压缩需要更多的计算和处理时间,因为它还要进行哈夫曼编码的步骤。因此,在压缩速度方面,Deflate 可能比 Gzip 更快。
- 应用场景:Gzip 压缩常用于文件压缩、网络传输和 HTTP 响应的内容编码。它广泛应用于 Web 服务器和浏览器之间的数据传输,以减小文件大小和提高网络传输效率。
何时使用Gzip,何时使用Deflate?
- 使用Gzip :当需要压缩文件以用于长期存储或者需要在客户端完整恢复文件的原始信息(如文件创建时间等元数据)时,gzip是更好的选择。此外,如果你的优先考虑是压缩效率而不是速度,或者压缩的文件较大(如日志文件、备份等),gzip通常会提供更优的压缩比率。
- 使用Deflate :在需要快速压缩和解压的场合,尤其是网络数据传输,如Web页面、API通信等,使用deflate更为合适。由于deflate不包含处理文件元数据的额外开销,它在这些场景下能提供更快的处理速度。
http请求压缩
- 
在使用http服务的时候,我们的端口一共有65536个选择,分别是0-65535,不能够超出65535 - 这个限制的来源是2的16次方-1,也可以理解为256x256,就是端口的极限
- 我们通过开启服务来模拟前端上传内容到服务器的效果,能够更直观去感受到
 
- 
且这里需要普及一个知识点: - 知名端口(Well-Known Ports) :从0到1023,这些端口号通常被系统或者常见的服务和应用程序使用。例如,HTTP服务的标准端口是80,HTTPS服务的标准端口是443。
- 注册端口(Registered Ports) :从1024到49151,这些端口号用于用户或应用程序。用户可以为自定义服务指定这些端口号。
- 动态或私有端口(Dynamic or Private Ports) :从49152到65535,这些端口号通常不由任何服务固定使用,而是由各种应用程序在运行时动态选择。
 - 在计算机网络中往往像上面这样进行区分,所以我们正常用来测试的端口是从1024到49151之间,挑没被用的进行使用就OK了
 
正常上传
            
            
              js
              
              
            
          
           const server = http.createServer((req, res) => {
   const txt = '小余'.repeat(1000);//重复1000遍内容
 
   //res.setHeader('Content-Encoding','gzip')
   // res.setHeader('Content-Encoding', 'deflate')
   res.setHeader('Content-type', 'text/plan;charset=utf-8')
 
   // const result = zlib.deflateSync(txt);
   res.end(txt)
 })
 
 server.listen(3000)- 1000个小余文本没有经过任何压缩处理,是6.2KB,这么一看还是挺大的

deflate压缩上传
            
            
              js
              
              
            
          
           const zlib = require('zlib'); 
 const http = require('node:http'); 
 //开启一个服务器
 const server = http.createServer((req,res)=>{
     const txt = '小余'.repeat(1000);
 
     //res.setHeader('Content-Encoding','gzip')
     res.setHeader('Content-Encoding','deflate')
     res.setHeader('Content-type','text/plan;charset=utf-8')//必须设置的请求头,防止内容乱码
    
     const result = zlib.deflateSync(txt);
     res.end(result)
 })
 
 server.listen(3000)//端口要小于65536,也就是小于等于65535- 
上面代码所输出的内容是通过deflate压缩的,我们通过控制台点开网络netWork选项,就能看到占据多少内容 - 就目前来看,1000个小余,一共是230B的大小
 

gzip压缩上传
            
            
              ini
              
              
            
          
           const zlib = require('zlib'); 
 const http = require('node:http'); 
 const server = http.createServer((req,res)=>{
     const txt = '小余'.repeat(1000);
 
     res.setHeader('Content-Encoding','gzip')
     //res.setHeader('Content-Encoding','deflate')
     res.setHeader('Content-type','text/plan;charset=utf-8')
    
     const result = zlib.gzipSync(txt);
     res.end(result)
 })
 
 server.listen(3000)- 我们在使用gzip的压缩方式看一下,是239B,比230B还是要大一点的

- 
但不管是gzip还是deflate,两种压缩模式上传服务器所花费的资源,都远远小于直接上传 - 在上面的案例中,压缩上传所占据的大小只有直接上传的3.6% ,这是非常NB的
- 而deflate的压缩效率会比gzip更高一点
 
- 在上面的案例中,