Comment réaliser une compression de contenu à l'aide de Nodejs ? L'article suivant parlera de la méthode d'implémentation de la compression de contenu (gzip/br/deflate) côté Node par la pratique. J'espère que cela vous sera utile !
Lorsque je vérifiais mon journal application, j'ai constaté que le chargement prenait toujours quelques secondes après être entré dans la page du journal (l'interface n'était pas paginée), j'ai donc ouvert le panneau réseau pour vérifier
ce n'est qu'à ce moment-là que j'ai découvert que les données renvoyées par l'interface n'étaient pas compressées. Je pensais que l'interface utilisait le proxy inverse Nginx, et Nginx m'aiderait automatiquement à faire cette couche (j'explorerai cela plus tard, c'est le cas). théoriquement réalisable)
Le backend ici est Node Service
Cet article partagera les connaissances sur la Compression de données HTTP
et la pratique du Côté nœud
HTTP数据压缩
相关知识以及在Node侧的实践
下面的客户端均指浏览器
客户端在向服务端发起请求时,会在请求头(request header)中添加accept-encoding
字段,其值标明客户端支持的压缩内容编码
格式
服务端在对返回内容执行压缩后,通过在响应头(response header)中添加content-encoding
,来告诉浏览器内容实际压缩使用的编码算法
deflate
是同时使用了LZ77
算法与哈夫曼编码(Huffman Coding)
的一个无损数据压缩算法。
gzip
是基于 DEFLATE
的算法
br
指代Brotli
,该数据格式旨在进一步提高压缩比,对文本的压缩相对deflate
能增加20%
的压缩密度,而其压缩与解压缩速度则大致不变
Node.js包含一个zlib 模块
,提供了使用 Gzip
、Deflate/Inflate
、以及 Brotli
实现的压缩功能
这里以gzip
为例分场景列举多种使用方式,Deflate/Inflate
与Brotli
使用方式一样,只是API不一样
基于stream
的操作
基于buffer
的操作
引入几个所需的模块
const zlib = require('zlib') const fs = require('fs') const stream = require('stream') const testFile = 'tests/origin.log' const targetFile = `${testFile}.gz` const decodeFile = `${testFile}.un.gz`
解/压缩结果查看,这里使用du
指令直接统计解压缩前后结果
# 执行 du -ah tests # 结果如下 108K tests/origin.log.gz 2.2M tests/origin.log 2.2M tests/origin.log.un.gz 4.6M tests
流(stream)
的操作使用createGzip
与createUnzip
zlib
API,除了那些显式同步的 API,都使用 Node.js 内部线程池,可以看做是异步的方式1: 直接利用实例上的pipe
方法传递流
// 压缩 const readStream = fs.createReadStream(testFile) const writeStream = fs.createWriteStream(targetFile) readStream.pipe(zlib.createGzip()).pipe(writeStream) // 解压 const readStream = fs.createReadStream(targetFile) const writeStream = fs.createWriteStream(decodeFile) readStream.pipe(zlib.createUnzip()).pipe(writeStream)
方式2: 利用stream
上的pipeline
,可在回掉中单独做其它的处理
// 压缩 const readStream = fs.createReadStream(testFile) const writeStream = fs.createWriteStream(targetFile) stream.pipeline(readStream, zlib.createGzip(), writeStream, err => { if (err) { console.error(err); } }) // 解压 const readStream = fs.createReadStream(targetFile) const writeStream = fs.createWriteStream(decodeFile) stream.pipeline(readStream, zlib.createUnzip(), writeStream, err => { if (err) { console.error(err); } })
方式3: Promise化pipeline
方法
const { promisify } = require('util') const pipeline = promisify(stream.pipeline) // 压缩 const readStream = fs.createReadStream(testFile) const writeStream = fs.createWriteStream(targetFile) pipeline(readStream, zlib.createGzip(), writeStream) .catch(err => { console.error(err); }) // 解压 const readStream = fs.createReadStream(targetFile) const writeStream = fs.createWriteStream(decodeFile) pipeline(readStream, zlib.createUnzip(), writeStream) .catch(err => { console.error(err); })
Buffer
的操作利用 gzip
与 unzip
API,这两个方法包含同步
与异步
类型
gzip
gzipSync
unzip
unzipSync
方式1: 将readStream
转Buffer
,然后进行进一步操作
// 压缩 const buff = [] readStream.on('data', (chunk) => { buff.push(chunk) }) readStream.on('end', () => { zlib.gzip(Buffer.concat(buff), targetFile, (err, resBuff) => { if(err){ console.error(err); process.exit() } fs.writeFileSync(targetFile,resBuff) }) })
// 压缩 const buff = [] readStream.on('data', (chunk) => { buff.push(chunk) }) readStream.on('end', () => { fs.writeFileSync(targetFile,zlib.gzipSync(Buffer.concat(buff))) })
方式2: 直接通过readFileSync
Les clients suivants font tous référence aux navigateurs
🎜Lorsque le client initie une requête au serveur, elle inclura l'en-tête de la requête. Ajoutez le champ
accept-encoding
dans l'(en-tête de la requête), dont la valeur indique le format de codage de contenu compressé pris en charge
par le client. 🎜l'algorithme d'encodage utilisé pour la compression réelle< en ajoutant <code>content -encoding
à l'en-tête de réponse /code>🎜deflate
utilise à la fois le LZ77
et Huffman Coding
sont un algorithme de compression de données sans perte. 🎜🎜gzip
est un algorithme basé sur DEFLATE
🎜🎜br
fait référence à Brotli
, qui est un format de données conçu pour améliorer encore le taux de compression, la compression du texte peut augmenter la densité de compression de 20 %
par rapport au dégonfler
, tandis que la vitesse de compression et de décompression reste à peu près la même🎜module zlib
, qui permet l'utilisation de Gzip
, Deflate/Inflate
, et Fonction de compression implémentée par Brotli
🎜🎜Ici, nous prenons gzip
comme exemple pour lister diverses méthodes d'utilisation selon les scénarios, Dégonfler/Dégonfler
et Brotli</code >L'utilisation est la même, mais l'API est différente🎜🎜<strong>Fonctionnement basé sur le <code>stream
🎜🎜🎜🎜Opérations basées sur le buffer</ code></strong>🎜🎜<img src="https://img.php.cn/upload/image/734/331/536/164674055924407Parlons de la façon dutiliser Node pour réaliser la compression de contenu grâce à la pratique" title="164674055924407Parlons de la façon dutiliser Node pour réaliser la compression de contenu grâce à la pratique" alt="Parlons de la façon dutiliser Node pour réaliser la compression de contenu grâce à la pratique "/>🎜🎜Introduire plusieurs modules requis 🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">// 压缩
const readBuffer = fs.readFileSync(testFile)
const decodeBuffer = zlib.gzipSync(readBuffer)
fs.writeFileSync(targetFile,decodeBuffer)
// 解压
const readBuffer = fs.readFileSync(targetFile)
const decodeBuffer = zlib.gzipSync(decodeFile)
fs.writeFileSync(targetFile,decodeBuffer)</pre><div class="contentsignin">Copier après la connexion</div></div><h2 data-id="heading-6">Décompression/compression de fichiers🎜🎜Afficher les résultats de décompression/compression, utilisez ici la commande <code>du
pour compter directement les résultats avant et après décompression🎜// 测试数据 const testData = fs.readFileSync(testFile, { encoding: 'utf-8' })
stream
createGzip
et createUnzip
🎜zlib
, à l'exception de celles qui sont explicitement synchrones, utilisent le pool de threads interne de Node.js et peuvent être considérées comme asynchronespipe< /code> sur l'instance pour transmettre le flux🎜 <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">const buffer = Buffer.from(testData)</pre><div class="contentsignin">Copier après la connexion</div></div><div class="contentsignin">Copier après la connexion</div></div><div class="contentsignin">Copier après la connexion</div></div><div class="contentsignin">Copier après la connexion</div></div>🎜<strong>Méthode 2 :</strong> En utilisant le <code>pipeline
sur stream
, vous pouvez effectuer d'autres traitements séparément pendant le rollback🎜const transformStream = new stream.PassThrough() transformStream.write(buffer) // or const transformStream = new stream.Duplex() transformStream.push(Buffer.from(testData)) transformStream.push(null)
pipeline
promise🎜transformStream .pipe(zlib.createGzip()) .pipe(fs.createWriteStream(targetFile))
Buffer</code ></h3>🎜Utilisez les API <code> gzip
et unzip
, ces deux méthodes incluent les types synchrone
et asynchrone
🎜< ul>gzip
gzipSync
décompresser
décompresserSync
readStream
vers Buffer
, puis effectuera d'autres opérations🎜const buffer = Buffer.from(testData)
const result = zlib.gzipSync(buffer)
fs.writeFileSync(targetFile, result)
readFileSync
🎜 🎜En plus de la compression des fichiers, parfois peut-être Pour décompresser directement le contenu transféré🎜这里以压缩文本内容为例
// 测试数据 const testData = fs.readFileSync(testFile, { encoding: 'utf-8' })
流(stream)
操作这块就考虑 string
=> buffer
=> stream
的转换就行
string
=> buffer
const buffer = Buffer.from(testData)
buffer
=> stream
const transformStream = new stream.PassThrough() transformStream.write(buffer) // or const transformStream = new stream.Duplex() transformStream.push(Buffer.from(testData)) transformStream.push(null)
这里以写入到文件示例,当然也可以写到其它的流里,如HTTP的Response
(后面会单独介绍)
transformStream .pipe(zlib.createGzip()) .pipe(fs.createWriteStream(targetFile))
Buffer
操作同样利用Buffer.from
将字符串转buffer
const buffer = Buffer.from(testData)
然后直接使用同步API进行转换,这里result就是压缩后的内容
const result = zlib.gzipSync(buffer)
可以写入文件,在HTTP Server
中也可直接对压缩后的内容进行返回
fs.writeFileSync(targetFile, result)
这里直接使用Node中 http
模块创建一个简单的 Server 进行演示
在其他的 Node Web
框架中,处理思路类似,当然一般也有现成的插件,一键接入
const http = require('http') const { PassThrough, pipeline } = require('stream') const zlib = require('zlib') // 测试数据 const testTxt = '测试数据123'.repeat(1000) const app = http.createServer((req, res) => { const { url } = req // 读取支持的压缩算法 const acceptEncoding = req.headers['accept-encoding'].match(/(br|deflate|gzip)/g) // 默认响应的数据类型 res.setHeader('Content-Type', 'application/json; charset=utf-8') // 几个示例的路由 const routes = [ ['/gzip', () => { if (acceptEncoding.includes('gzip')) { res.setHeader('content-encoding', 'gzip') // 使用同步API直接压缩文本内容 res.end(zlib.gzipSync(Buffer.from(testTxt))) return } res.end(testTxt) }], ['/deflate', () => { if (acceptEncoding.includes('deflate')) { res.setHeader('content-encoding', 'deflate') // 基于流的单次操作 const originStream = new PassThrough() originStream.write(Buffer.from(testTxt)) originStream.pipe(zlib.createDeflate()).pipe(res) originStream.end() return } res.end(testTxt) }], ['/br', () => { if (acceptEncoding.includes('br')) { res.setHeader('content-encoding', 'br') res.setHeader('Content-Type', 'text/html; charset=utf-8') // 基于流的多次写操作 const originStream = new PassThrough() pipeline(originStream, zlib.createBrotliCompress(), res, (err) => { if (err) { console.error(err); } }) originStream.write(Buffer.from('<h1>BrotliCompress</h1>')) originStream.write(Buffer.from('<h2>测试数据</h2>')) originStream.write(Buffer.from(testTxt)) originStream.end() return } res.end(testTxt) }] ] const route = routes.find(v => url.startsWith(v[0])) if (route) { route[1]() return } // 兜底 res.setHeader('Content-Type', 'text/html; charset=utf-8') res.end(`<h1>404: ${url}</h1> <h2>已注册路由</h2> <ul> ${routes.map(r => `<li><a href="${r[0]}">${r[0]}</a></li>`).join('')} </ul> `) res.end() }) app.listen(3000)
更多node相关知识,请访问:nodejs 教程!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!