您好,欢迎访问一九零五行业门户网

通过实践来聊聊利用Node怎么实现内容压缩

利用nodejs怎么实现内容压缩?下面本篇文章给大家通过实践来聊聊node侧实现内容压缩(gzip/br/deflate)的方法,希望对大家有所帮助!
在查看自己的应用日志时,发现进入日志页面后总是要几秒钟才会加载(接口没做分页),于是打开网络面板查看
这才发现接口返回的数据都没有被压缩,本以为接口用nginx反向代理了,nginx会自动帮我做这一层(这块后面探究一下,理论上是可行的)
这里的后端是 node 服务
本文就分享一下 http数据压缩相关知识以及在node侧的实践
前置知识下面的客户端均指浏览器
accept-encoding
客户端在向服务端发起请求时,会在请求头(request header)中添加accept-encoding字段,其值标明客户端支持的压缩内容编码格式
content-encoding
服务端在对返回内容执行压缩后,通过在响应头(response header)中添加content-encoding,来告诉浏览器内容实际压缩使用的编码算法
deflate/gzip/brdeflate是同时使用了lz77算法与哈夫曼编码(huffman coding)的一个无损数据压缩算法。
gzip 是基于 deflate 的算法
br指代brotli,该数据格式旨在进一步提高压缩比,对文本的压缩相对deflate能增加20%的压缩密度,而其压缩与解压缩速度则大致不变
zlib模块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.gz2.2m tests/origin.log2.2m tests/origin.log.un.gz4.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,这两个方法包含同步与异步类型
压缩gzipgzipsync解压unzipunzipsync方式1: 将readstream转buffer,然后进行进一步操作
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)// orconst 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相关知识,请访问:nodejs 教程!
以上就是通过实践来聊聊利用node怎么实现内容压缩的详细内容。
其它类似信息

推荐信息