HTTP 报文压缩
根据 HTTP 协议,服务器返回内容给浏览器会尝试把 response 内容进行压缩,在浏览器解压后使用,以提升传输速度。工作原理主要是
- 浏览器发送请求时通过 request header
accept-encoding标识支持的压缩格式 - 服务端从列表中选择一种用来对响应内容压缩,并通过 response header
content-encoding指明使用的格式 - 浏览器得到响应正文后,依据
content-encoding进行解压Node.js 压缩
Node.js 内容压缩主要通过内置的zlib模块实现,zlib 提供了几种压缩方法,和 accept-encoding 的对应关系
| accept-encoding | zlib |
|---|---|
| gzip | zlib.createGzip() |
| deflate | zlib.createDeflate() |
| br | zlib.createBrotliCompress() |
zlib 几个方法创建的对象都是 Transform 流,使用非常简单
const { createGzip } = require('zlib');const { createReadStream, createWriteStream } = require('fs');const gzip = createGzip();const source = createReadStream('input.txt');const destination = createWriteStream('input.txt.gz');source.pipe(gzip).pipe(destination);
判断内容是否需要压缩
因为压缩有利于提升 Web 性能,所以尽可能对数据进行压缩,但压缩是一个有 CPU 开销的任务,有些文件类型本身已经压缩过,比如 jpeg 图片,无需再次压缩消耗 CPU,甚至再次压缩可能导致体积变大
可以安装、使用模块 compressible 判断模块是否需要压缩
npm install -S compressible
const compressible = require('compressible');// 参数是 MIMEcompressible('text/html'); // truecompressible('image/png'); // false
简单实现
为了实现功能需要引用几个新的模块, accepts 模块比直接使用 req.headers 方便很多
const zlib = require('zlib');const compressible = require('compressible');const accepts = require('accepts');
对返回文件内容部分逻辑做简单修改
const contentType = mime.contentType(path.extname(url));let compression;if (compressible(contentType)) {const encodings = accepts(req).encodings();const serverCompatibleCompressions = [{ method: 'gzip', stream: zlib.createGzip() },{ method: 'deflate', stream: zlib.createDeflate() },{ method: 'br', stream: zlib.createBrotliCompress() },];// 按照浏览器指定优先级在服务器选择压缩方式for (let i = 0; i < encodings.length; i++) {compression = serverCompatibleCompressions.find(com => com.method === encodings[i]);if (compression) {break;}}}if (compression) {res.writeHead(200, {'Content-Type': contentType,// 指定服务器使用的压缩方式,浏览器使用对应的解压方式'Content-Encoding': compression.method,});fs.createReadStream(filePath).pipe(compression.stream).pipe(res);} else {res.writeHead(200, {'Content-Type': contentType,});fs.createReadStream(filePath).pipe(res);}

原体积 607k,压缩后变成了 139k,对传输性能优化非常明显
代码完整参考:https://github.com/Samaritan89/static-server/tree/v3
变更部分:https://github.com/Samaritan89/static-server/commit/ed99886e29cf656ce96e1e333f6e8823640e18f8
