Maison  >  Article  >  interface Web  >  Parlons de la façon d'utiliser Node pour réaliser la compression de contenu grâce à la pratique

Parlons de la façon d'utiliser Node pour réaliser la compression de contenu grâce à la pratique

青灯夜游
青灯夜游avant
2022-03-08 20:00:582023parcourir

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 !

Parlons de la façon d'utiliser Node pour réaliser la compression de contenu grâce à la pratique

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

Parlons de la façon dutiliser Node pour réaliser la compression de contenu grâce à la pratique

Parlons de la façon dutiliser Node pour réaliser la compression de contenu grâce à la pratique

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œudHTTP数据压缩相关知识以及在Node侧的实践

前置知识

下面的客户端均指浏览器

accept-encoding

Parlons de la façon dutiliser Node pour réaliser la compression de contenu grâce à la pratique

客户端在向服务端发起请求时,会在请求头(request header)中添加accept-encoding字段,其值标明客户端支持的压缩内容编码格式

content-encoding

Parlons de la façon dutiliser Node pour réaliser la compression de contenu grâce à la pratique

服务端在对返回内容执行压缩后,通过在响应头(response header)中添加content-encoding,来告诉浏览器内容实际压缩使用的编码算法

deflate/gzip/br

deflate是同时使用了LZ77算法与哈夫曼编码(Huffman Coding)的一个无损数据压缩算法。

gzip 是基于 DEFLATE 的算法

br指代Brotli,该数据格式旨在进一步提高压缩比,对文本的压缩相对deflate能增加20%的压缩密度,而其压缩与解压缩速度则大致不变

zlib模块

Node.js包含一个zlib 模块,提供了使用 GzipDeflate/Inflate、以及 Brotli 实现的压缩功能

这里以gzip为例分场景列举多种使用方式,Deflate/InflateBrotli使用方式一样,只是API不一样

基于stream的操作

Parlons de la façon dutiliser Node pour réaliser la compression de contenu grâce à la pratique

基于buffer的操作

Parlons de la façon dutiliser Node pour réaliser la compression de contenu grâce à la pratique

引入几个所需的模块

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)的操作

使用createGzipcreateUnzip

  • 注:所有 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的操作

利用 gzipunzip API,这两个方法包含同步异步类型

  • 压缩
    • gzip
    • gzipSync
  • 解压
    • unzip
    • unzipSync

方式1:readStreamBuffer,然后进行进一步操作

  • gzip:异步
// 压缩
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)
    })
})
  • gzipSync:同步
// 压缩
const buff = []
readStream.on('data', (chunk) => {
    buff.push(chunk)
})
readStream.on('end', () => {
    fs.writeFileSync(targetFile,zlib.gzipSync(Buffer.concat(buff)))
})

方式2: 直接通过readFileSync

Connaissances préalables

Les clients suivants font tous référence aux navigateurs

accepter-encodage

Parlons de la façon dutiliser Node pour réaliser la compression de contenu grâce à la pratique

🎜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. 🎜

contenu -encodage

🎜Parlons de la façon dutiliser Node pour réaliser la compression de contenu grâce à la pratique🎜🎜Une fois que le serveur a effectué la compression sur le contenu renvoyé, il indique au contenu du navigateur l'algorithme d'encodage utilisé pour la compression réellecontent -encoding à l'en-tête de réponse /code>🎜

deflate/gzip/br

🎜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 🎜🎜Node.js contient un 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 BrotliL'utilisation est la même, mais l'API est différente🎜🎜Fonctionnement basé sur le stream🎜🎜Parlons de la façon dutiliser Node pour réaliser la compression de contenu grâce à la pratique🎜🎜Opérations basées sur le buffer code>🎜🎜Parlons de la façon dutiliser Node pour réaliser la compression de contenu grâce à la pratique 🎜🎜Introduire plusieurs modules requis 🎜
// 压缩
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)

Décompression/compression de fichiers🎜🎜Afficher les résultats de décompression/compression, utilisez ici la commande du pour compter directement les résultats avant et après décompression🎜
// 测试数据
const testData = fs.readFileSync(testFile, { encoding: 'utf-8' })

Opérations basées sur stream

🎜Utilisez createGzip et createUnzip 🎜
  • Remarque : Toutes les API 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 asynchrones
  • Donc ce qui suit Le code de compression et de décompression dans l'exemple doit être exécuté séparément, sinon une erreur sera signalée
🎜Méthode 1 : Utilisez directement le pipe sur l'instance pour transmettre le flux🎜 <pre class="brush:js;toolbar:false;">const buffer = Buffer.from(testData)</pre>🎜<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)
🎜Méthode 3 : Méthode pipeline promise🎜
transformStream
    .pipe(zlib.createGzip())
    .pipe(fs.createWriteStream(targetFile))

Opération basée sur Buffer

🎜Utilisez les API gzip et unzip, ces deux méthodes incluent les types synchrone et asynchrone🎜
  • Compression
    • gzip
    • gzipSync
  • Décompresser
    • décompresser
    • décompresserSync
  • 🎜 Méthode 1 : readStream vers Buffer, puis effectuera d'autres opérations🎜
    • gzip : asynchrone
    const buffer = Buffer.from(testData)
    • gzipSync : synchronisé
    const result = zlib.gzipSync(buffer)
    🎜Méthode 2 : Lire 🎜
    fs.writeFileSync(targetFile, result)
    🎜décompression/compression du contenu texte directement via 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: &#39;utf-8&#39; })

    基于流(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 Server中的实践

    这里直接使用Node中 http 模块创建一个简单的 Server 进行演示

    在其他的 Node Web 框架中,处理思路类似,当然一般也有现成的插件,一键接入

    Parlons de la façon dutiliser Node pour réaliser la compression de contenu grâce à la pratique

    const http = require(&#39;http&#39;)
    const { PassThrough, pipeline } = require(&#39;stream&#39;)
    const zlib = require(&#39;zlib&#39;)
    
    // 测试数据
    const testTxt = &#39;测试数据123&#39;.repeat(1000)
    
    const app = http.createServer((req, res) => {
        const { url } = req
        // 读取支持的压缩算法
        const acceptEncoding = req.headers[&#39;accept-encoding&#39;].match(/(br|deflate|gzip)/g)
    
        // 默认响应的数据类型
        res.setHeader(&#39;Content-Type&#39;, &#39;application/json; charset=utf-8&#39;)
    
        // 几个示例的路由
        const routes = [
            [&#39;/gzip&#39;, () => {
                if (acceptEncoding.includes(&#39;gzip&#39;)) {
                    res.setHeader(&#39;content-encoding&#39;, &#39;gzip&#39;)
                    // 使用同步API直接压缩文本内容
                    res.end(zlib.gzipSync(Buffer.from(testTxt)))
                    return
                }
                res.end(testTxt)
            }],
            [&#39;/deflate&#39;, () => {
                if (acceptEncoding.includes(&#39;deflate&#39;)) {
                    res.setHeader(&#39;content-encoding&#39;, &#39;deflate&#39;)
                    // 基于流的单次操作
                    const originStream = new PassThrough()
                    originStream.write(Buffer.from(testTxt))
                    originStream.pipe(zlib.createDeflate()).pipe(res)
                    originStream.end()
                    return
                }
                res.end(testTxt)
            }],
            [&#39;/br&#39;, () => {
                if (acceptEncoding.includes(&#39;br&#39;)) {
                    res.setHeader(&#39;content-encoding&#39;, &#39;br&#39;)
                    res.setHeader(&#39;Content-Type&#39;, &#39;text/html; charset=utf-8&#39;)
                    // 基于流的多次写操作
                    const originStream = new PassThrough()
                    pipeline(originStream, zlib.createBrotliCompress(), res, (err) => {
                        if (err) {
                            console.error(err);
                        }
                    })
                    originStream.write(Buffer.from(&#39;<h1>BrotliCompress</h1>&#39;))
                    originStream.write(Buffer.from(&#39;<h2>测试数据</h2>&#39;))
                    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(&#39;Content-Type&#39;, &#39;text/html; charset=utf-8&#39;)
        res.end(`<h1>404: ${url}</h1>
        <h2>已注册路由</h2>
        <ul>
            ${routes.map(r => `<li><a href="${r[0]}">${r[0]}</a></li>`).join(&#39;&#39;)}
        </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!

    Déclaration:
    Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer