如何在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
相关产品推荐
相关产品推荐

