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

如何在Koa.js中使用ReadableStream等Web Streams响应请求?

问题

我正在使用Node.js 18提供的原生fetch调用API,该API返回的响应体是ReadableStream实例。我需要将该响应直接通过Koa.js服务器传递给前端,无需对fetch获取的响应做任何处理,管道传输流是最高效的方式。以下是简化后的代码:

import Koa from 'koa';

const app = new Koa();

app.use(async (ctx) => {
  
  const response = await fetch('http://api-server:8181');

  // 如何让Web Streams在此生效?
  ctx.body = response.body;
});

问题在于ctx.body接受stream.Readable实例,但不接受标准Web Streams API的ReadableStream。我该如何解决这个问题?

注:之前在Node v16中,我使用node-fetch polyfill,它为了便利使用stream.Readable而非ReadableStream,这与标准行为明显不同,但当时实现起来更简单,现在情况变了。


解决方案

方法1:使用Readable.fromWeb()转换Web Stream(推荐)

Node.js 18及以上版本内置了stream.Readable.fromWeb()方法,可直接将Web Streams API的ReadableStream转换为Node.js原生的stream.Readable实例,完美适配Koa的ctx.body要求。

修改后的代码如下:

import Koa from 'koa';
import { Readable } from 'stream';

const app = new Koa();

app.use(async (ctx) => {
  const response = await fetch('http://api-server:8181');
  
  // 转换Web Stream为Node.js可读流
  ctx.body = Readable.fromWeb(response.body);
  
  // 同步源API的响应头和状态码(可选但推荐)
  ctx.status = response.status;
  response.headers.forEach((value, key) => {
    ctx.set(key, value);
  });
});

方法2:手动读取Web Stream并推送至Node.js流(兼容旧版本)

如果需要兼容更早的Node.js版本(非必须,Node.js 18+优先用方法1),可以通过Web Stream的getReader()手动读取数据,再推送到Node.js可读流中:

import Koa from 'koa';
import { Readable } from 'stream';

const app = new Koa();

app.use(async (ctx) => {
  const response = await fetch('http://api-server:8181');
  const webStream = response.body;
  
  // 创建空的Node.js可读流
  const nodeStream = new Readable({ read() {} });
  
  ctx.body = nodeStream;
  ctx.status = response.status;
  response.headers.forEach((value, key) => {
    ctx.set(key, value);
  });

  // 处理Web Stream数据
  const reader = webStream.getReader();
  const processChunk = async () => {
    const { done, value } = await reader.read();
    if (done) {
      nodeStream.push(null); // 标记流结束
      return;
    }
    nodeStream.push(value);
    await processChunk();
  };

  // 捕获读取过程中的错误
  processChunk().catch(err => nodeStream.emit('error', err));
});

说明:方法1是官方推荐的最优方案,代码简洁且性能最佳。同步源API的响应头和状态码能让前端获得与直接调用源API一致的响应体验。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 12:10:24