Nodejs를 사용하여 콘텐츠 압축을 달성하는 방법은 무엇입니까? 다음 글에서는 Node 측에서 컨텐츠 압축(gzip/br/deflate)을 구현하는 방법에 대해 실습을 통해 이야기해보겠습니다.
application로그를 확인할 때 로그 페이지에 들어간 후 로드하는 데 항상 몇 초가 걸린다는 사실을 발견했습니다(인터페이스는 페이지가 매겨져 있지 않았습니다). 그래서 네트워크 패널을 열어
를 확인했습니다.
이제서야 인터페이스에서 반환된 데이터가 압축되지 않은 것을 발견했습니다. 인터페이스가 Nginx 역방향 프록시를 사용하고 Nginx가 자동으로 이 레이어를 수행하는 데 도움이 될 것이라고 생각했습니다. 이론적으로 가능함)
여기 백엔드는 Node Service
이 기사에서는 HTTP 데이터 압축
에 대한 지식과 노드 측
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
다음 클라이언트는 모두 브라우저를 참조합니다
🎜클라이언트가 서버에 대한 요청을 시작하면 요청 헤더가 포함됩니다. (요청 헤더)에
accept-encoding
필드를 추가합니다. 이 필드의 값은 클라이언트의 지원되는 압축 콘텐츠 인코딩
형식을 나타냅니다. 🎜콘텐츠를 추가하여 브라우저 콘텐츠에 <code>실제 압축에 사용되는 인코딩 알고리즘을 응답 헤더에 추가합니다. /code>🎜<h3 data-id="heading-4">deflate/gzip/br</h3>🎜<code>deflate
는 LZ77
알고리즘과 Huffman Coding
은 무손실 데이터 압축 알고리즘입니다. 🎜🎜gzip
은 DEFLATE
를 기반으로 한 알고리즘입니다. 🎜🎜br
은 Brotli
를 의미합니다. 압축 비율을 더욱 향상시키기 위해 텍스트 압축은 deflate
에 비해 압축 밀도를 20%
높일 수 있으며 압축 및 압축 해제 속도는 거의 동일하게 유지됩니다🎜Gzip
, Deflate/Inflate
사용을 제공하는 zlib 모듈
이 포함되어 있습니다. , Brotli에서 구현한 압축 기능
🎜🎜여기에서는 시나리오에 따른 다양한 사용 방법을 나열하기 위해 gzip
을 예로 들어 Deflate/Inflate
및 Brotli
사용법은 같지만 API가 다릅니다🎜🎜스트림
기반 작업🎜🎜🎜🎜버퍼 기반 작업 코드>
🎜🎜🎜🎜몇 가지 필수 모듈 소개 🎜// 压缩 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)
du
명령을 사용하여 직접 계산 압축 풀기 전과 후의 결과🎜// 测试数据 const testData = fs.readFileSync(testFile, { encoding: 'utf-8' })
stream
createGzip
및 createUnzip 사용
🎜zlib
API는 Node.js 내부 스레드 풀을 사용하며 비동기식으로 간주될 수 있습니다.파이프
를 직접 사용합니다. > 스트림을 전달하기 위한 인스턴스의 메소드🎜 const buffer = Buffer.from(testData)🎜방법 2:
스트림
의 파이프라인
을 사용하면 스트림 중에 별도로 다른 처리를 수행할 수 있습니다. Rollback🎜const transformStream = new stream.PassThrough() transformStream.write(buffer) // or const transformStream = new stream.Duplex() transformStream.push(Buffer.from(testData)) transformStream.push(null)🎜방법 3 :
파이프라인
메소드 약속🎜transformStream .pipe(zlib.createGzip()) .pipe(fs.createWriteStream(targetFile))
버퍼
기반 작업🎜 gzip
및 unzip
API를 활용하세요. 이 두 가지 방법에는 동기
및 비동기
유형이 포함됩니다🎜gzip
gzipSync
unzip
unzipSync
readStream
을 버퍼
로 지정한 다음 추가 작업을 수행합니다🎜const buffer = Buffer.from(testData)
const result = zlib.gzipSync(buffer)🎜방법 2:
readFileSync
🎜🎜In을 통해 직접 텍스트 콘텐츠의 🎜fs.writeFileSync(targetFile, result)🎜압축 풀기/압축 읽기 파일 압축 외에도 때로는 전송된 콘텐츠의 압축을 직접 풀기 위해🎜
这里以压缩文本内容为例
// 测试数据 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 教程!
위 내용은 실습을 통해 Node를 사용하여 콘텐츠 압축을 달성하는 방법에 대해 이야기해 보겠습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!