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

如何在Node.js中向客户端返回OpenAI流式响应

解决Node.js中OpenAI流式响应无法正确返回给客户端的问题

问题核心

在Node.js环境中调用OpenAI流式API时,虽然能正常获取分片数据,但使用toJSON(stream)序列化流返回给客户端会导致接收结果异常——客户端拿到的是流的结构描述,而非实际的流式文本内容。这是因为ReadableStream是不可序列化的对象,无法通过JSON转换传递实际数据。

错误原因

你当前的代码中使用res.status(200).send(toJSON(stream)),本质上是把Web标准的ReadableStream对象序列化为JSON字符串,客户端收到的只是流的元数据(比如内部状态、构造函数信息),而非逐步推送的文本分片。

而Next.js的API路由支持直接返回new Response(stream),是因为Next.js会自动将Web标准流适配为HTTP响应的分块传输,无需手动序列化。

解决方案

在Node.js中,需要将Web标准的ReadableStream转换为Node.js原生的Readable流,然后通过管道(pipe)传输给客户端,并设置正确的响应头确保流式传输生效。

步骤1:转换Web流到Node.js流

添加一个转换函数,将Web的ReadableStream转换为Node.js的stream.Readable:

import { Readable } from 'stream';
import { ReadableStream } from 'web-streams-polyfill/ponyfill/es2018';

/**
 * 将Web标准ReadableStream转换为Node.js原生Readable流
 * @param webStream Web标准ReadableStream
 * @returns Node.js Readable流
 */
function webStreamToNodeStream(webStream) {
  const reader = webStream.getReader();
  const nodeStream = new Readable({
    async read() {
      try {
        const { done, value } = await reader.read();
        if (done) {
          this.push(null);
          return;
        }
        this.push(value);
      } catch (err) {
        this.destroy(err);
      }
    },
  });
  return nodeStream;
}

步骤2:修改接口处理函数

移除toJSON序列化,改用管道传输流,并设置正确的响应头:

// 移除import { toJSON } from 'flatted';

export const fetchChatOpenAI = async (req, res) => {
  try {
    const stream = await OpenAIStream(
      model,
      promptToSend,
      temperatureToUse,
      key,
      messagesToSend
    );

    // 设置流式响应的必要头信息
    res.setHeader('Content-Type', 'text/plain; charset=utf-8');
    res.setHeader('Transfer-Encoding', 'chunked');
    // 如果使用Server-Sent Events(SSE)协议,替换为以下头信息:
    // res.setHeader('Content-Type', 'text/event-stream');
    // res.setHeader('Cache-Control', 'no-cache');
    // res.setHeader('Connection', 'keep-alive');

    // 转换流并管道传输给客户端
    const nodeStream = webStreamToNodeStream(stream);
    nodeStream.pipe(res);

    // 处理流结束和错误
    nodeStream.on('end', () => res.end());
    nodeStream.on('error', (err) => {
      console.error(err);
      res.status(500).end('Stream transmission failed');
    });
  } catch (error) {
    if (error instanceof OpenAIError) {
      console.error(error);
      res.status(500).json({ statusText: error.message });
    } else {
      res.status(500).json({ statusText: 'ERROR' });
    }
  }
};

步骤3:客户端代码无需修改

你当前的客户端代码已经正确处理了流式响应(通过response.body.getReader()读取分片),保持现有逻辑即可。

验证效果

修改后,客户端会逐步接收到OpenAI返回的文本分片,日志输出会和Next.js示例一致——每次reader.read()都会返回对应的文本块,而非序列化后的流对象。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 02:02:31