如何利用 CompressionStream 异步压缩并在网络上传输海量原始 DOM 状态快照

梦瑶君_3588

梦瑶君_3588

2026-05-13

254人浏览

原创

compressionstream 是现代浏览器原生支持的流式压缩 api,适用于 dom 快照实时压缩上传,具备零拷贝、异步流式、自动分片和无 polyfill 依赖等优势。

如何利用 compressionstream 异步压缩并在网络上传输海量原始 dom 状态快照

CompressionStream 是现代浏览器(Chrome 109+、Firefox 113+、Safari 17.4+)原生支持的可读/可写流压缩 API,专为 Web 环境设计,无需引入第三方库或手动管理 Worker,天然适配 fetch、ReadableStream 和 TransformStream。它特别适合将大型 DOM 状态快照(如序列化后的完整页面结构、组件树、表单数据、Canvas 像素缓冲等)在发送前实时压缩,显著降低上传带宽与延迟。


✅ 为什么用 CompressionStream 而不是 Blob + gzip 工具?

  • 零内存拷贝:直接管道式处理,避免将整个快照转成 ArrayBuffer 或 Blob 再压缩(易触发 OOM);
  • 真正异步流式:边序列化、边压缩、边上传,三阶段重叠,不阻塞主线程;
  • 自动 chunk 分片:配合 fetch() 的 body: ReadableStream,浏览器底层按需分块推送,服务端可边收边解压;
  • 无 polyfill 依赖:纯标准 API,兼容性明确,未来维护成本低。

? 步骤一:生成轻量但完整的 DOM 快照流

不要用 document.documentElement.outerHTML(体积大、含冗余样式/脚本)。推荐:

  • 使用 document.cloneNode(true) + 清理策略(移除 script、style、data-* 等非状态属性);
  • 或更优:用 serializeDOM() 类函数只提取关键状态(input.value、textarea.value、checked、selectedIndex、dataset.state 等),输出为精简 JSON 流;
async function* domSnapshotStream() {
  const state = {
    url: location.href,
    timestamp: Date.now(),
    inputs: Array.from(document.querySelectorAll('input, textarea, select'))
      .reduce((acc, el) => {
        acc[el.name || el.id] = el.type === 'checkbox' ? el.checked : el.value;
        return acc;
      }, {})
  };
  yield new TextEncoder().encode(JSON.stringify(state));
}

✅ 输出是 AsyncIterable<uint8array></uint8array>,可直接接入 CompressionStream。

Upload audio to AIOZ Stream
Upload audio to AIOZ Stream

快速上传音频至 AIOZ Stream API。支持默认或自定义编码配置创建音频对象,上传文件并完成处理后返回音频链接。

下载

? 步骤二:构建压缩传输流水线

async function uploadCompressedSnapshot() {
  const snapshotStream = domSnapshotStream();
  const compressedStream = new CompressionStream('gzip');

  // 管道:snapshot → compress → upload
  const readable = (async function* () {
    for await (const chunk of snapshotStream) {
      yield chunk;
    }
  })();

  const body = readable
    .pipeThrough(compressedStream)
    .pipeThrough(new TextEncoderStream()); // 可选:确保最终为 Uint8Array 流

  const res = await fetch('/api/snapshot', {
    method: 'POST',
    headers: {
      'Content-Encoding': 'gzip',
      'Content-Type': 'application/json'
    },
    body
  });

  return res.ok;
}

⚠️ 注意:

  • Content-Encoding: gzip 必须显式设置,否则服务端无法识别压缩格式;
  • body 是 ReadableStream<uint8array></uint8array>,fetch 自动处理流式上传;
  • 不需要 await 整个流结束才发请求 —— 流一旦启动,上传立即开始。

? 步骤三:服务端配合(以 Node.js Express 为例)

const zlib = require('zlib');

app.post('/api/snapshot', express.raw({ 
  type: '*/*', 
  limit: '50mb' 
}), (req, res) => {
  const encoding = req.get('content-encoding')?.toLowerCase();
  let stream = req;

  if (encoding === 'gzip') {
    stream = req.pipe(zlib.createGunzip());
  }

  let data = '';
  stream.on('data', chunk => data += chunk);
  stream.on('end', () => {
    try {
      const snapshot = JSON.parse(data);
      // 存储、分析、回放...
      res.status(200).send('ok');
    } catch (e) {
      res.status(400).send('invalid json');
    }
  });
});

✅ 关键点:

  • 用 express.raw() 接收原始字节流(不能用 json() 或 text() 中间件);
  • 根据 Content-Encoding 动态解压,保持流式处理;
  • 全程不缓存整包,内存占用恒定(~几 KB buffer)。

⚙️ 进阶建议(提升可靠性与可观测性)

  • 添加简单校验:在快照 JSON 外层包裹 { version: 2, checksum: 'xx', data: { ... } },压缩前计算 sha256 片段;
  • 超时控制:用 AbortController 包裹 fetch,防止卡死长流;
  • 错误回退:检测 CompressionStream 不可用时(旧浏览器),降级为 JSON.stringify() + Blob + FormData;
  • 监控压缩率:通过 compressedStream.readable.locked 和 writable.closed 配合计时器,统计 originalSize / compressedSize。

不需要打包、不依赖 WebAssembly、不新建 Worker —— 利用浏览器原生流能力,就能安全、高效、渐进式地上传海量 DOM 快照。

相关专题

更多
C语言变量命名
C语言变量命名

c语言变量名规则是:1、变量名以英文字母开头;2、变量名中的字母是区分大小写的;3、变量名不能是关键字;4、变量名中不能包含空格、标点符号和类型说明符。php中文网还提供c语言变量的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

2989

3

c语言入门自学零基础
c语言入门自学零基础

C语言是当代人学习及生活中的必备基础知识,应用十分广泛,本专题为大家c语言入门自学零基础的相关文章,以及相关课程,感兴趣的朋友千万不要错过了。

2023.07.25

2228

9

c语言运算符的优先级顺序
c语言运算符的优先级顺序

c语言运算符的优先级顺序是括号运算符 > 一元运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符。本专题为大家提供c语言运算符相关的各种文章、以及下载和课程。

2023.08.02

1200

5

c语言数据结构
c语言数据结构

数据结构是指将数据按照一定的方式组织和存储的方法。它是计算机科学中的重要概念,用来描述和解决实际问题中的数据组织和处理问题。数据结构可以分为线性结构和非线性结构。线性结构包括数组、链表、堆栈和队列等,而非线性结构包括树和图等。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.09

1138

4

c语言random函数用法
c语言random函数用法

c语言random函数用法:1、random.random,随机生成(0,1)之间的浮点数;2、random.randint,随机生成在范围之内的整数,两个参数分别表示上限和下限;3、random.randrange,在指定范围内,按指定基数递增的集合中获得一个随机数;4、random.choice,从序列中随机抽选一个数;5、random.shuffle,随机排序。

2023.09.05

1336

5

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

2023.09.20

2078

7

c语言get函数的用法
c语言get函数的用法

get函数是一个用于从输入流中获取字符的函数。可以从键盘、文件或其他输入设备中读取字符,并将其存储在指定的变量中。本文介绍了get函数的用法以及一些相关的注意事项。希望这篇文章能够帮助你更好地理解和使用get函数 。

2023.09.20

3280

8

c数组初始化的方法
c数组初始化的方法

c语言数组初始化的方法有直接赋值法、不完全初始化法、省略数组长度法和二维数组初始化法。详细介绍:1、直接赋值法,这种方法可以直接将数组的值进行初始化;2、不完全初始化法,。这种方法可以在一定程度上节省内存空间;3、省略数组长度法,这种方法可以让编译器自动计算数组的长度;4、二维数组初始化法等等。

2023.09.22

14695

6

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

2023.09.22

549

3

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
CentOS 官方文档
CentOS 官方文档

共0课时 | 0人学习

极客学院Java8新特性视频教程
极客学院Java8新特性视频教程

共17课时 | 4.3万人学习