首页前端开发其他前端知识Node实现内容压缩的方法及详细代码是什么

Node实现内容压缩的方法及详细代码是什么

时间2024-03-26 02:08:03发布访客分类其他前端知识浏览1665
导读:在实际案例的操作过程中,我们可能会遇到“Node实现内容压缩的方法及详细代码是什么”这样的问题,那么我们该如何处理和解决这样的情况呢?这篇小编就给大家总结了一些方法,具有一定的借鉴价值,希望对大家有所帮助,接下来就让小编带领大家一起了解看看...
在实际案例的操作过程中,我们可能会遇到“Node实现内容压缩的方法及详细代码是什么”这样的问题,那么我们该如何处理和解决这样的情况呢?这篇小编就给大家总结了一些方法,具有一定的借鉴价值,希望对大家有所帮助,接下来就让小编带领大家一起了解看看吧。

在查看自己的应用日志时,发现进入日志页面后总是要几秒钟才会加载(接口没做分页),于是打开网络面板查看

这才发现接口返回的数据都没有被压缩,本以为接口用Nginx反向代理了,Nginx会自动帮我做这一层(这块后面探究一下,理论上是可行的)

这里的后端是 Node 服务

本文就分享一下 HTTP数据压缩相关知识以及在Node侧的实践

前置知识

下面的客户端均指浏览器

accept-encoding

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

content-encoding

服务端在对返回内容执行压缩后,通过在响应头(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的操作

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

使用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读取

// 压缩
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)

文本内容的解/压缩

除了对文件压缩,有时候也许要对传输的内容进行直接进行解压缩

这里以压缩文本内容为例

// 测试数据
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 Server中的实践

这里直接使用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实现内容压缩的方法及详细代码是什么的介绍啦,需要的朋友可以参考上述内容,希望对大家有帮助,欢迎关注网络,小编将为大家输出更多高质量的实用文章!

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!


若转载请注明出处: Node实现内容压缩的方法及详细代码是什么
本文地址: https://pptw.com/jishu/653189.html
Java"弱" 引用如何理解,有哪些基础知识 JavaScript实现队列的操作是什么,有什么要注意的

游客 回复需填写必要信息