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

如何无需等待完整结果,逐段读取流式响应?

解决方案

要实现实时接收ChatGPT的流式响应,需要同时调整后端响应头和前端的处理逻辑,具体步骤如下:

一、后端调整

当前后端设置的application/octet-stream是用于文件下载的类型,不适合流式推送,需要修改响应头适配流式传输格式:

const chatStream = await fetch("https://api.openai.com/v1/chat/completions", {
  method: "POST",
  body: JSON.stringify({
    model: process.env.CHAT_GPT_MODEL,
    messages,
    n: 1,
    max_tokens: parseInt(process.env.CHAT_GPT_TOKEN_LIMIT ?? "2000"),
    stream: true,
  }),
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer " + process.env.CHAT_GPT_API_KEY,
  },
});

// 修改为流式响应头
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache"); // 禁止缓存响应片段
res.setHeader("Connection", "keep-alive"); // 保持长连接

// 继续管道转发响应
chatStream.body.pipe(res);

同时去掉原本的Content-Disposition头,避免浏览器触发自动下载行为。

二、前端调整

方式1:使用原生Fetch API(推荐)

Angular的HttpClient默认会等待完整响应才返回结果,原生Fetch支持直接读取ReadableStream,能精准处理每个返回的响应片段:

async function handleStreamResponse() {
  const url = this.getUrl('/create-new-chat-gpt-room');
  const token = this.auth?.loginToken;
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`
    },
    body: JSON.stringify({ title })
  });

  if (!response.body) {
    throw new Error('响应流为空');
  }

  const reader = response.body.getReader();
  const decoder = new TextDecoder('utf-8');
  
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    
    // 把二进制片段解码为文本
    const chunk = decoder.decode(value);
    // 处理ChatGPT的流式格式:每行以data:开头,最终以[DONE]标记结束
    const lines = chunk.split('\n').filter(line => line.trim() && line.startsWith('data:'));
    
    for (const line of lines) {
      const data = line.slice(5).trim();
      if (data === '[DONE]') continue;
      
      try {
        const json = JSON.parse(data);
        const content = json.choices[0].delta.content;
        if (content) {
          console.log(content); // 实时打印每一段返回内容
        }
      } catch (err) {
        console.error('解析响应失败:', err);
      }
    }
  }
}

// 调用处理函数
handleStreamResponse().catch(err => console.error('请求出错:', err));

方式2:使用Angular HttpClient

如果坚持用Angular的HttpClient,需要开启进度报告并监听下载事件:

import { HttpEventType } from '@angular/common/http';

// ...

this.http.post(this.getUrl('/create-new-chat-gpt-room'), { title }, {
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${this.auth?.loginToken}`
  },
  responseType: 'text',
  observe: 'events',
  reportProgress: true // 开启进度报告,获取分段响应
}).pipe(
  catchError(this.catchError.bind(this))
).subscribe(event => {
  // 监听下载进度事件,获取部分响应文本
  if (event.type === HttpEventType.DownloadProgress && event.partialText) {
    const chunk = event.partialText;
    const lines = chunk.split('\n').filter(line => line.trim() && line.startsWith('data:'));
    
    for (const line of lines) {
      const data = line.slice(5).trim();
      if (data === '[DONE]') continue;
      
      try {
        const json = JSON.parse(data);
        const content = json.choices[0].delta.content;
        if (content) {
          console.log(content);
        }
      } catch (err) {
        console.error('解析失败:', err);
      }
    }
  } else if (event.type === HttpEventType.Response) {
    // 响应全部完成后的收尾处理
  }
});

核心原因

你之前使用Blob和FileReader的方式,会等待整个响应完全下载到本地Blob后才触发onload事件,自然无法实时获取分段内容。改用流式读取后,能在每个响应片段到达时立即处理,实现实时打印的需求。

内容的提问来源于stack exchange,提问作者Alvin Stefanus

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 02:47:06