把它做成生产功能之前,需要先拆开协议事实、产品承诺和运行成本,三者混在一起很容易得到错误结论。 网页无法像内核 sendfile 那样把文件描述符直接交给网络栈。所谓零拷贝通常是减少 JavaScript 堆上的重复副本:按窗口切片、把 ArrayBuffer 转移给 Worker、处理后及时释放引用,并避免把整文件拼成一个 Blob。
文件传输要同时对账发送偏移、接收落盘、分块校验和双方终态。进度条到达百分之百,只能说明字节进入队列,不能证明文件完整可用。
落地方案的关键部件
先把下面几项写成可评审的规则,而不是散落在回调和界面判断里。规则越清楚,后续扩容、兼容和排错越少靠猜。
- 读取窗口与发送窗口共用字节预算,未确认数据不能无限提前读取。
- Worker 之间用 transferable ArrayBuffer 转移所有权,转移后原线程不再访问。
- 接收端分块持久化并流式计算摘要,完成前不创建第二份整文件内存副本。
浏览器里更诚实的目标是有界、少复制的流水线。只要内存峰值由窗口控制、取消能释放、摘要和存储可流式处理,就能稳定传超大文件。
别让错误假设进入生产
边界条件会把隐藏假设变成真实事故。弱网、刷新、并发和容量上限必须组合测试,因为单独出现时它们往往都能被重试掩盖。
- 同一块为哈希、加密和发送分别 slice,峰值内存变成窗口的数倍。
- Worker 转移后仍复用 detached buffer,偶发发送零长度或抛异常。
- 取消任务只停网络循环,读取队列和 Worker 仍持有大量缓冲。
发布闸门应该检查什么
测试不能以“最后成功了”结束。还要测量经历了多少次副作用、等待多久、是否泄露内容,以及失败后再次执行能否从干净基线开始。
- 传输数倍于设备内存的虚拟文件,确认峰值随窗口而非文件大小增长。
- 在每个流水线阶段取消并强制 GC,检查缓冲引用、Worker 和存储事务全部释放。
- 对比 transferable 与复制消息的 CPU、内存和吞吐,选择真实收益而不是口号。
可以上线的标准是:用户知道当前发生了什么,系统在异常后能停止或恢复,资源不会无界增长,运维也能从最小必要证据定位阶段。