发布于 2026-01-06 0 阅读
0

JSON compression in the browser, with gzip and the Compression Streams API. DEV's Worldwide Show and Tell Challenge Presented by Mux: Pitch Your Projects!

在浏览器中使用 gzip 和 Compression Streams API 进行 JSON 压缩。

由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!

编辑: 只是 Firefox 还不支持。

介绍

在构建 Web 应用程序时,我们有时需要持久化大型 JSON 对象。这可以通过Web Storage直接保存到浏览器,也可以通过FileFetch API 从外部保存。

这个问题让我不禁思考:存储原始JSON还是编码后的JSON,有没有更好的解决方案?我们能否实现一定程度的压缩?

事实证明,答案是肯定的,而且使用压缩流 API实现起来出奇地简单

此示例是为满足Web StorageFileSolid Pod持久化的特定需求而创建的,用于扩展 JSON 应用程序状态的表示。

背景

简单介绍一下背景,我目前负责的项目不与服务器通信。应用程序数据以事务方式组织在默克尔树(类似 Git 或区块链)中。每次与应用程序交互时,树状结构都会自然增长,内存和存储空间占用也会随之增加。

为了密切关注数据增长情况,我在控制栏中添加了一个小指示器来可视化数据大小。我发现随着 JSON 对象不断增长,数据量开始达到一个让我不太放心的程度,所以我决定研究一下,看看能否找到更好的解决方案。

943kb 原始 JSON

这让我接触到了压缩流 API。这是一个浏览器 API,可以直接在浏览器中gzip对流媒体进行压缩。ReadableStream

TL;DRgzip启用JSON 数据压缩后,我发现数据大小减少了 98%,从943kb减少到10kb

10kb 压缩后的 JSON

这并非科学测试,数据也无法像在应用程序中自然扩展那样进行扩展,但它可以作为一个公平的基准,表明我可以通过此 API 获得我想要的结果。

解决方案

由于压缩流 API是流 API的一部分,我们首先需要将 JSON 数据对象转换为ReadableStream

可以从文本构建 Blob,也可以将 JSON 序列化为文本,因此我们可以从 JSON 创建一个Blob 从中获取我们的流。

// Convert JSON to Stream
const stream = new Blob([JSON.stringify(data)], {
    type: 'application/json',
}).stream();
Enter fullscreen mode Exit fullscreen mode

现在我们有了数据ReadableStream,我们可以用这个ReadableStream.pipeThrough方法将数据通过gzip CompressionStream转换进行传输。

// gzip stream
const compressedReadableStream = stream.pipeThrough(
    new CompressionStream("gzip")
);
Enter fullscreen mode Exit fullscreen mode

然后,为了将此压缩流返回到我们的主代码,我们可以创建一个新的响应,并像处理从Fetch API收到的任何其他数据一样处理数据

// create Response
const compressedResponse = 
  await new Response(compressedReadableStream);
Enter fullscreen mode Exit fullscreen mode

现在数据已经压缩完毕,我们该如何处理它呢?

实际上,相当多。

我们可以将其进行 base64 编码,然后存储起来localStorage

// Get response Blob
const blob = await compressedResponsed.blob();
// Get the ArrayBuffer
const buffer = await blob.arrayBuffer();

// convert ArrayBuffer to base64 encoded string
const compressedBase64 = btoa(
  String.fromCharCode(
    ...new Uint8Array(buffer)
  )
);

// Set in localStorage
localStorage.setItem('compressedData', compressedBase64);
Enter fullscreen mode Exit fullscreen mode

我们可以将其保存为文件

// Get response Blob
const blob = await compressedResponse.blob();

// Create a programmatic download link
const elem = window.document.createElement("a");
elem.href = window.URL.createObjectURL(blob);
elem.download = '';
document.body.appendChild(elem);
elem.click();
document.body.removeChild(elem);
Enter fullscreen mode Exit fullscreen mode

或者我们可以采用更传统的方法,通过 HTTPS 和Fetch API发送。

// Get response Blob
const blob = await compressedResponse.blob();

await fetch(url, {
  method: 'POST',
  body: blob,
  headers: {
    'Content-Encoding': 'gzip',
  }
});
Enter fullscreen mode Exit fullscreen mode

我相信还有许多其他技术可以用于我们新压缩的 JSON 对象。

很酷吧?但是我们如何将数据转换成浏览器中可读的形式呢?

要解压缩,我们首先需要将压缩后的数据放回一个文件中ReadableStream

可以直接从我们的Blob中获取(或从文件/获取响应中获取

const stream = compressedResponse.blob().stream();
Enter fullscreen mode Exit fullscreen mode

或者从 base64 解码回来。

// base64 encoding to Blob
const stream = new Blob([b64decode(compressedBase64)], {
  type: "application/json",
}).stream();
Enter fullscreen mode Exit fullscreen mode

编辑:我使用的 b64 解码函数如下所示。

export function b64decode(str: string): ArrayBuffer {
  const binary_string = window.atob(str);
  const len = binary_string.length;
  const bytes = new Uint8Array(new ArrayBuffer(len));
  for (let i = 0; i < len; i++) {
    bytes[i] = binary_string.charCodeAt(i);
  }
  return bytes;
}
Enter fullscreen mode Exit fullscreen mode

然后,这次我们使用压缩后的数据ReadableStream,再次执行该ReadableStream.pipeThrough方法,将数据通过管道传递给它gzip DecompressionStream

const compressedReadableStream = stream.pipeThrough(
  new DecompressionStream("gzip")
);
Enter fullscreen mode Exit fullscreen mode

然后我们回到响应中,获取Blob并调用该Blob.text()方法以字符串格式获取内容。

  const resp = await new Response(compressedReadableStream);
  const blob = await resp.blob();
Enter fullscreen mode Exit fullscreen mode

现在只需将此字符串解析回 JSON,我们就得到了解压缩后的对象,它又回到了 Javascript 中,可以正常使用了。

const data = JSON.parse(await blob.text());
Enter fullscreen mode Exit fullscreen mode

演示和结果

我搭建了一个小型CodeSandbox,它接收一些在线 JSON 源,并按照gzip CompressionStream上述解决方案中描述的转换方法运行它们。

哦,对了。❤️ VueJS。

结论

首先,我非常享受撰写这篇文章和探索压缩流 API 的过程。它使用起来很简单,感觉像是对 Javascript 生态系统的一个非常好的补充。

就 API 本身的实用性而言,它确实帮我解决了减少持久数据占用空间的问题。

已经把这个功能集成到我的应用中,但我计划很快将其更深入地整合到核心功能中。这应该会是另一个有趣的问题,因为该应用已经使用Ageragerage-wasm))集成了客户端加密。不过,那是以后的事了……

根据CanIUse 的数据,该 API 目前的支持率仅为 72.53%,尚未完全支持,这确实有点令人失望。但我仍然认为它非常值得开发。我们可以实现当压缩流 API不受支持时优雅地失败,从而放弃性能提升,所以这对我来说并非致命缺陷。

我不确定压缩流 API会成为我使用频率多高的工具,但我很高兴学习了它,而且我相信这不会是我最后一次使用它,或者至少会考虑使用它。

文章来源:https://dev.to/ternentdotdev/json-compression-in-the-browser-with-gzip-and-the-compression-streams-api-4135