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

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:

  1. 安装依赖:
npm install next-formidable formidable
  1. 修改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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 05:24:57