WebSocket send方法不支持流控,需手动分片并基于bufferedAmount和readyState实现背压控制:先切片(如64KB)、检查连接状态、发送后等待bufferedAmount回落再发下一片,避免内存溢出。

WebSocket 的 send 方法本身不支持分块上传或内置流控,发送大文件时若直接调用 send(blob) 或 send(arrayBuffer),容易因网络慢、接收方处理滞后,导致数据在浏览器内部缓冲区(bufferedAmount)持续堆积,最终触发内存压力甚至页面卡死。关键不是“能不能发”,而是“怎么控制发”。
理解 bufferedAmount 的真实含义
bufferedAmount 表示**已调用 send() 但尚未被底层 TCP 栈真正发出的字节数**(注意:不是已送达,也不是待接收,是“发出去的路上还没走完”的量)。它只反映浏览器内部发送队列长度,不包含 OS 内核缓冲区或网络中间设备积压。当该值持续增长(比如 > 5MB),说明发送速度远超网络承载能力,必须暂停。
分片 + 节流:手动实现背压控制
不能依赖 WebSocket 自动限速,必须自己切片、轮询、等待。核心逻辑是:每次只发一块,等 bufferedAmount 降到安全阈值再发下一块。
- 将文件按固定大小(如 64KB)切分为
Uint8Array或Blob片段 - 维护一个发送队列和当前偏移量,用
send()发送当前片 - 监听
socket.onopen和socket.onclose状态,避免发向断开连接 - 用
requestIdleCallback或setTimeout(..., 0)避免阻塞主线程
用 readyState 和 bufferedAmount 协同判断是否可继续发送
仅靠 bufferedAmount 不够 —— 连接未就绪(readyState !== 1)时调用 send() 会静默失败。正确做法是:
立即学习“Java免费学习笔记(深入)”;
- 每次准备发片前,先检查
socket.readyState === WebSocket.OPEN - 再检查
socket.bufferedAmount - 两者都满足才发;否则延迟重试(推荐用
setInterval每 50ms 查一次,比轮询更稳) - 发送后立即更新进度,避免重复发同一片
实际发送时优先用 ArrayBuffer,而非 Blob
Blob 在某些浏览器中会触发额外拷贝或延迟解析,增大内存压力。更稳妥的方式是:
- 用
file.arrayBuffer().then(buf => new Uint8Array(buf))提前读取为ArrayBuffer - 用
slice()切出子数组:chunk = fullArray.subarray(start, end) - 直接
socket.send(chunk.buffer)—— 零拷贝、可控、无隐式转换 - 若服务端要求二进制类型,确保服务端能识别
ArrayBuffer(绝大多数 WebSocket 服务都支持)
不复杂但容易忽略:bufferedAmount 是动态指标,必须结合连接状态和主动节流才能真正防爆内存。单纯“等它变小”不如“发一块、等确认、再发下一块”来得可靠。


















