通过Next.js API Routes使用EventSource流式传输时一次性接收所有数据块
问题分析与修复方案
你的实现存在两个核心问题,并非Next.js对EventSource的特殊处理,而是SSE格式不规范+Next.js默认响应缓冲导致的流式失效:
1. 未遵循SSE协议格式规范
EventSource要求每个数据块必须以data: 前缀开头,且以\n\n作为分隔符,浏览器才会识别为独立的事件并触发message回调。你直接将原始chunk写入响应,浏览器会将所有内容攒到一起才解析,自然无法分批接收。
2. Next.js默认响应缓冲
Next.js的API路由(尤其是Pages Router)默认会缓冲响应内容,直到响应结束才一次性发送给客户端,直接阻断了流式传输的能力。
修复后的服务端代码(Pages Router)
res.writeHead(200, { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive", "X-Accel-Buffering": "no", // 禁用反向代理(如Nginx)的缓冲 }); answerStream.data .on("data", (chunk: string) => { // 按SSE规范包装数据块 res.write(`data: ${chunk}\n\n`); // 强制刷新响应,绕过Next.js的缓冲机制 if (res.flush) res.flush(); }) .on("error", (error: Error) => { console.error(error); res.write(`data: [DONE]\n\n`); res.end(); }) .on("end", () => { res.write(`data: [DONE]\n\n`); res.end(); });
若使用Next.js 13+ App Router,推荐用StreamingResponse实现
import { StreamingResponse } from 'next/server'; export const runtime = 'edge'; // 必须使用Edge Runtime,避免Node.js环境的缓冲 export async function GET() { const stream = new ReadableStream({ start(controller) { answerStream.data .on("data", (chunk: string) => { controller.enqueue(`data: ${chunk}\n\n`); }) .on("error", (err) => { controller.error(err); }) .on("end", () => { controller.enqueue(`data: [DONE]\n\n`); controller.close(); }); }, }); return new StreamingResponse(stream, { headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive", "X-Accel-Buffering": "no", }, }); }
额外注意事项
- 本地开发时若仍有缓冲问题,可尝试构建生产包测试,或在
next.config.js中给流式接口添加专属头:
module.exports = { async headers() { return [ { source: '/api/your-stream-endpoint', headers: [ { key: 'Cache-Control', value: 'no-cache' }, { key: 'Connection', value: 'keep-alive' }, { key: 'X-Accel-Buffering', value: 'no' }, ], }, ]; }, };
- 部署到Vercel时,必须使用Edge Runtime(App Router)或在Pages Router中禁用缓冲,否则平台默认的Node.js运行时会拦截流式响应。
内容的提问来源于stack exchange,提问作者Gh05d
相关产品推荐
相关产品推荐

