Next.js 13 App Router使用multer上传图片失败,req.file为undefined求助
问题核心原因
你遇到的req.file为undefined的根本问题是multer与Next.js 13 App Router的API路由不兼容。
multer是专为Express框架设计的中间件,依赖Node.js原生http模块的IncomingMessage请求对象;而Next.js 13 App Router的API路由采用Web标准的Request(即NextRequest)对象,两者的请求结构、解析逻辑完全不同,导致multer无法正确解析App Router中的文件上传请求。
解决方案
以下是两种可行的替代方案,无需使用multer:
方案1:原生手动解析FormData
直接利用Web标准的formData()方法解析请求,手动处理文件存储:
修改API路由代码:
import { NextRequest, NextResponse } from "next/server"; import { ulid } from 'ulid'; import fs from 'fs/promises'; import path from 'path'; export async function POST(request: NextRequest) { const formData = await request.formData(); const file = formData.get('file') as File | null; if (!file) { return NextResponse.json({ error: '未上传文件' }, { status: 400 }); } // 处理文件存储 const fileBuffer = await file.arrayBuffer(); const fileName = `${ulid()}-${file.name}`; const filePath = path.join(process.cwd(), 'public/uploads', fileName); // 确保uploads目录存在 await fs.mkdir(path.join(process.cwd(), 'public/uploads'), { recursive: true }); await fs.writeFile(filePath, Buffer.from(fileBuffer)); console.log('文件上传成功:', { path: `/uploads/${fileName}`, originalname: file.name }); return NextResponse.json({ message: "文件上传成功", file: { path: `/uploads/${fileName}`, originalname: file.name } }); }
同时修改前端axios请求,移除手动设置的Content-Type(axios会自动根据FormData生成包含boundary的正确请求头,手动设置会导致解析失败):
axios.post('/api/uploader', data) .then(res => { setMessage(JSON.stringify(res.data.message, null, 2)); console.log(JSON.stringify(res?.data, null, 2)); }).catch(err => { console.log('err', err); setMessage('上传失败: '); });
方案2:使用Next.js适配的文件上传库(next-formidable)
如果需要类似multer的便捷语法,可以使用专门适配Next.js的next-formidable:
- 安装依赖:
npm install next-formidable formidable
- 修改API路由:
import { NextRequest, NextResponse } from "next/server"; import { parseForm } from 'next-formidable'; import { ulid } from 'ulid'; import path from 'path'; export const config = { api: { bodyParser: false, // 禁用默认body解析,交给formidable处理 }, }; export async function POST(request: NextRequest) { const { fields, files } = await parseForm(request, { uploadDir: path.join(process.cwd(), 'public/uploads'), filename: (name, ext, part) => `${ulid()}-${part.originalFilename}`, filter: ({ mimetype }) => ['image/jpeg', 'image/png', 'image/jpg'].includes(mimetype || ''), maxFileSize: 1024 * 1024, // 限制1MB }); const file = files.file as any; if (!file) { return NextResponse.json({ error: '未上传文件或文件类型不合法' }, { status: 400 }); } console.log('文件信息:', { path: file.filepath, originalname: file.originalFilename }); return NextResponse.json({ message: "文件上传成功", file: { path: `/uploads/${file.newFilename}`, originalname: file.originalFilename } }); }
前端同样需要移除手动设置的Content-Type请求头。
额外注意事项
- App Router的API路由默认启用Edge Runtime,而multer依赖Node.js的
fs、http等模块,Edge Runtime不支持这些模块,这也是multer无法使用的另一原因。 - 存储文件到
public/uploads时,务必确保目录存在,否则会触发写入错误,可通过fs.mkdir的recursive: true参数自动创建目录。
内容的提问来源于stack exchange,提问作者Yankz Kuyateh
相关产品推荐
相关产品推荐

