You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Web应用发送HTTP分块数据,客户端能否完整接收每个分块?

问题解答

必须担忧这种情况——依赖read()调用对应单个HTTP分块的行为完全不可靠,生产环境一定会出问题。

原因

HTTP分块传输编码只是规定了服务器如何将响应拆分成多个分块发送,但浏览器的fetch API在暴露ReadableStream时,不会保证每个read()的返回值对应一个HTTP分块。浏览器会根据自身的缓冲区管理、网络传输情况,自由合并多个HTTP分块为一次read()结果,或者将单个HTTP分块拆分成多次read()调用返回——这完全符合HTTP规范,属于正常的实现行为。

会出现两种直接导致代码失效的场景:

  • 两个HTTP分块被合并:服务器发送的{"data":1}和{"data":2}两个分块,被浏览器合并成{"data":1}{"data":2}返回给read(),此时JSON.parse会直接抛出语法错误。
  • 单个HTTP分块被拆分:服务器发送的{"data":123}分块,被拆成两次read(),第一次返回{"data":1,第二次返回23},两次解析都会失败。

低成本解决方案(避免大幅复杂化)

不需要彻底重构前后端,只要约定一个分隔符(比如换行符\n),客户端做缓冲拼接后按分隔符拆分完整的JSON块再解析即可。

修改后的客户端代码

const response = await fetch("/server_api");
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';

while (true) {
  const {value, done} = await reader.read();
  if (done) {
    // 处理最后剩余的缓冲内容
    if (buffer.trim()) {
      try {
        const obj = JSON.parse(buffer);
        // 在这里处理解析后的对象
      } catch (e) {
        console.error('末尾无效JSON:', e);
      }
    }
    break;
  }
  
  // 增量解码并追加到缓冲
  buffer += decoder.decode(value, {stream: true});
  // 按换行符拆分出完整的JSON块
  const chunks = buffer.split('\n');
  // 最后一个元素可能是不完整的,留到下一次循环处理
  buffer = chunks.pop();
  
  // 逐个处理完整的JSON块
  for (const chunk of chunks) {
    if (!chunk.trim()) continue; // 跳过空行
    try {
      const obj = JSON.parse(chunk);
      // 在这里处理解析后的对象
    } catch (e) {
      console.error('无效JSON块:', chunk, e);
    }
  }
}

后端对应修改

只需要在每个JSON数据包末尾添加一个换行符\n,再作为HTTP分块发送即可。比如原本发送JSON.stringify(obj),现在改成JSON.stringify(obj) + '\n'——这个改动极小,几乎不会增加复杂度。

这种方式本质上遵循了JSON Lines格式规范,是处理流式JSON数据的通用低成本方案。

内容的提问来源于stack exchange,提问作者Paul Moore

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 11:33:47