通过实践来聊聊利用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 模块,提供了使用 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@H_342_126@基于
流(stream)的操作使用createGzip与createUnzip
- 注:所有
zlibAPI,除了那些显式同步的 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怎么实现内容压缩的详细内容,更多请关注其它相关文章!
声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!
若转载请注明出处: 通过实践来聊聊利用Node怎么实现内容压缩
本文地址: https://pptw.com/jishu/593610.html
