Node.js 压缩与 zlib:gzip、brotli 与流式管线

Stream 篇说过大文件要走流;压缩场景里如果 先读全再压,内存照样炸。
zlib(以及 brotli 等)在 Node 里通常以 Transform 流Promise 版一次性 API 出现。
这一篇想讲清楚几件事:什么时候在 Node 里做压缩、流式管线怎么接、以及和反向代理 gzip 的分工

常见路径:

  • HTTP 响应Content-Encoding: gzip(不少框架或中间件帮你做);
  • 落盘归档日志打包大 JSON 上传前压缩
  • 与对象存储 / CDN 对接时,源站或构建步骤里 先压好再传

另一层现实:

  • Nginx / CDN 往往已经 对文本类资源做 gzip/br,Node 里再压一遍可能是 重复劳动——要按架构决定 压缩发生在哪里
  • gzip:兼容最广,流式支持成熟;
  • deflate:细节与 HTTP 里的 raw deflate vs zlib wrapper 容易混,跨语言对接时要 对齐 RFC
  • brotli:同样体积下往往 更小,但 压缩更吃 CPU,适合 预生成静态资源可接受延迟的 API

Node 里 API 名以 当前文档为准createGzipzlib.brotliCompress 等),版本间可能有 异步 Promise 形式 的补充。

典型模式:

  • readable.pipe(zlib.createGzip()).pipe(writable)
  • stream.promises.pipeline 统一处理 错误与关闭

和 Stream + 背压篇的关系:

  • Transform 在中间既读又写,背压 仍然从下游传回上游;
  • 不要data 里手动拼大 Buffer 再 write,除非你知道自己在干什么。

已知不大Buffer / 字符串,可以用 gzipSync / gzip(Promise) 等:

  • 代码短;
  • 但要注意 输入大小上限阻塞事件循环(Sync 版本)。

大对象仍应 流式Worker

若在 Node 里 自己 gzip 响应体

  • 要设 Content-Encoding: gzip
  • 注意 ETag / 缓存压缩后内容 一致,否则中间缓存会糊涂。

若前面 反向代理统一压缩

  • 应用往往输出 明文,由代理 协商 Accept-Encoding——少一层逻辑,但要 对齐调试(到底谁压的)。
  • 大流量 优先 流式 + pipeline,守住 内存与背压
  • 算法 在 gzip / brotli 之间按 兼容、CPU、体积 权衡;
  • 和网关分工 要想清楚,避免 双重压缩缓存错乱

zlibHTTP、Stream 两篇叠起来看,处理上传下载、静态资源、日志归档会顺很多。