Next.js 13 API POST用Busboy流式传大文件遇req.pipe报错求助
解决Next.js API路由中Busboy流式文件上传的
req.pipe is not a function问题 问题原因
在Next.js 13+的App Router中,API路由的req对象是Web Streams API的ReadableStream,而非Node.js原生Stream对象,因此没有pipe()方法。直接使用Readable.fromWeb(req.body)转换后,若未配置流式处理,Next.js会先将整个请求体加载到内存中,无法实现真正的大文件流式上传。
正确解决方案
1. 启用路由动态处理
在API路由文件顶部导出dynamic配置,强制Next.js以流式方式处理请求,避免缓存请求体:
export const dynamic = 'force-dynamic';
2. 完整实现代码
通过转换Web Stream为Node.js Stream,配合Busboy完成流式文件上传,同时确保等待处理完成后再返回响应:
import { Readable } from 'node:stream'; import { createWriteStream } from 'node:fs'; import { join } from 'node:path'; import { NextResponse } from 'next/server'; import busboy from 'busboy'; // 强制动态处理,保证请求体以流式传递 export const dynamic = 'force-dynamic'; export async function POST(req) { return new Promise((resolve, reject) => { // 将Web Headers对象转换为普通对象,适配Busboy要求 const headers = Object.fromEntries(req.headers.entries()); const bb = busboy({ headers }); bb.on('file', (fieldname, fileStream, info) => { const { filename } = info; console.log(`开始上传文件: ${filename}`); // 流式写入本地文件 const savePath = join(process.cwd(), 'public/uploads', filename); const writeStream = createWriteStream(savePath); fileStream.pipe(writeStream); writeStream.on('finish', () => { console.log(`文件 ${filename} 上传完成`); }); writeStream.on('error', (err) => { console.error(`文件写入失败: ${err}`); reject(err); }); }); bb.on('finish', () => { resolve(NextResponse.json({ success: true, message: '所有文件上传完成' })); }); bb.on('error', (err) => { console.error(`Busboy处理出错: ${err}`); reject(NextResponse.json({ success: false, error: err.message }, { status: 500 })); }); // 转换Web Stream并传递给Busboy Readable.fromWeb(req.body).pipe(bb); }); }
关键细节说明
dynamic: 'force-dynamic':必须配置,否则Next.js会预读取整个请求体到内存,导致流式处理失效。- Headers转换:Next.js的
req.headers是Web API的Headers对象,需用Object.fromEntries()转为普通对象才能被Busboy解析。 - Promise包裹:流式处理为异步操作,需用Promise等待Busboy的
finish事件触发后再返回响应,避免请求提前终止。 - 流式写入:直接将Busboy返回的文件流pipe到本地写入流,实现边传边写,不会占用过多内存。
内容的提问来源于stack exchange,提问作者Jian Shang Quan
相关产品推荐
相关产品推荐

