← 文章 开发感想

Cloudflare Workers 部署 50MB WASM 的踩坑记录

记录将一个 50MB 的八字演算 WASM 部署到 Cloudflare Workers 的完整过程,以及如何用 Brotli 压缩解决文件大小限制。

術數工具

基于 WebAssembly,本地演算,无需联网

Cloudflare Workers 有一个隐藏的限制:单个静态资源文件不能超过 25MB

我的八字演算 WASM 文件有 50MB。

这是一个需要解决的问题。

问题分析

WASM 文件大的原因很简单:八字演算需要内置大量的历法数据(节气精确时刻、真太阳时修正系数等),这些数据被直接编译进了二进制文件。

解决方案有三种:

  1. 拆分数据:将历法数据从 WASM 中剥离,作为独立的 JSON 文件按需加载。
  2. 压缩传输:保持 WASM 不变,使用 Brotli 或 Gzip 压缩后传输,浏览器自动解压。
  3. 换平台:使用支持更大文件的存储服务(如 R2)托管 WASM。

方案一改动最大,需要修改 Rust 源码。方案三引入了额外的复杂度。我选择了方案二。

Brotli 压缩的效果

# 原始大小
ls -lh ganzhi.wasm
# -rwxr-xr-x  50M  ganzhi.wasm

# Brotli 压缩
brotli -q 11 ganzhi.wasm -o ganzhi.wasm.br
ls -lh ganzhi.wasm.br
# -rwxr-xr-x  12M  ganzhi.wasm.br

压缩率达到 76%,12MB 完全在 Cloudflare 的 25MB 限制之内。

Worker 配置

关键在于 Worker 需要为 .wasm.br 文件设置正确的响应头,告诉浏览器这是一个 Brotli 压缩的 WASM 文件:

if (pathname.endsWith('.wasm.br')) {
  const response = await env.ASSETS.fetch(request);
  const headers = new Headers(response.headers);
  headers.set('Content-Type', 'application/wasm');
  headers.set('Content-Encoding', 'br');
  headers.set('Cache-Control', 'public, max-age=86400');
  return new Response(response.body, { status: response.status, headers });
}

浏览器收到 Content-Encoding: br 后,会自动解压并将结果作为 WASM 传递给 WebAssembly.instantiate()。整个过程对前端代码完全透明。

结果

最终部署成功,八字演算工具正常运行。用户下载的是 12MB 的压缩文件,浏览器解压后得到 50MB 的 WASM,整个过程约需 5-15 秒(取决于网络速度)。

有时候,最优雅的解决方案不是重构,而是在正确的地方加一个正确的响应头。


相关工具已部署上线,可以在上方的「八字命盘」入口体验完整演算功能。