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

Nestjs+Multer文件上传:React客户端请求报400且file为undefined

NestJS Multer 图片上传接口:React客户端调用返回400问题排查与解决

问题描述

我基于NestJS + Multer开发了用户照片上传接口,Postman测试完全正常,但通过React客户端上传时,后端返回400 Bad Request,控制器中console.log('file from inside controller: ', file);输出file为undefined。

后端代码

user.controller.ts

@Post('/upload-photo/:id')
@Serialize()
@Roles(UserRole.ADMIN)
@UseInterceptors(
  FileInterceptor('file', {
    storage: diskStorage({
      destination: './public',
      filename: (req, file, cb) => {
        const filename: string = file.originalname;
        const extension: string = path.parse(file.originalname).ext;
        console.log('file from multer: ', file);

        cb(null, `${filename}`);
      },
    }),
  }),
)
async uploadPhoto(@UploadedFile() file, @Param('id') id: string) {
  console.log('file from inside controller: ', file);
  return this.usersService.uploadPhoto(file, id);
}

user.service.ts

async uploadPhoto(
  file: Express.Multer.File,
  id: string,
): Promise<ICustomResponse> {
  try {
    const { path, destination, filename, originalname } = file;

    const modifiedPathSlashes = originalname;

    const user = await this.prisma.user.update({
      where: { id: id },
      data: { photo: modifiedPathSlashes },
    });
    console.log(user);
    return getCustomResponse({
      status: HttpStatus.OK,
      success: true,
      data: user,
    });
  } catch (error) {}
  throw new BadRequestException(
    getCustomResponse({
      status: HttpStatus.BAD_REQUEST,
      success: false,
      errors: 'Could not update user avatar',
    }),
  );
}

原因分析

核心问题是React客户端请求格式不符合Multer要求,同时后端异常处理逻辑掩盖了真实错误:

  1. Multer仅解析multipart/form-data类型请求,且要求表单字段名与FileInterceptor('file')中的参数完全一致
  2. React端可能未使用FormData构造请求体,或字段名不匹配
  3. 后端service的try/catch直接吞掉错误,无法定位具体问题

解决方案

前端(React)修正

必须用FormData构造请求,且字段名与后端保持一致:

const handleUpload = async (file, userId) => {
  const formData = new FormData();
  // 字段名必须和后端FileInterceptor('file')的参数一致
  formData.append('file', file);

  try {
    const response = await fetch(`/api/users/upload-photo/${userId}`, {
      method: 'POST',
      headers: {
        // 不要手动设置Content-Type,浏览器会自动添加正确的multipart/form-data边界
        'Authorization': 'Bearer ' + token // 需身份验证时添加
      },
      body: formData
    });
    const data = await response.json();
    console.log(data);
  } catch (err) {
    console.error(err);
  }
};

后端(NestJS)优化

  1. 修复异常捕获逻辑:不要吞掉错误,打印错误信息便于排查
async uploadPhoto(
  file: Express.Multer.File,
  id: string,
): Promise<ICustomResponse> {
  try {
    const { path, destination, filename, originalname } = file;

    const modifiedPathSlashes = originalname;

    const user = await this.prisma.user.update({
      where: { id: id },
      data: { photo: modifiedPathSlashes },
    });
    console.log(user);
    return getCustomResponse({
      status: HttpStatus.OK,
      success: true,
      data: user,
    });
  } catch (error) {
    // 打印错误信息,快速定位问题
    console.error('上传失败:', error);
    throw new BadRequestException(
      getCustomResponse({
        status: HttpStatus.BAD_REQUEST,
        success: false,
        errors: 'Could not update user avatar',
      }),
    );
  }
}
  1. 添加文件校验(可选):提前拦截非法文件,避免后续错误
@UseInterceptors(
  FileInterceptor('file', {
    storage: diskStorage({
      destination: './public',
      filename: (req, file, cb) => {
        const filename: string = file.originalname;
        console.log('file from multer: ', file);
        cb(null, `${filename}`);
      },
    }),
    // 限制文件类型为图片
    fileFilter: (req, file, cb) => {
      if (!file.mimetype.startsWith('image/')) {
        return cb(new BadRequestException('仅支持图片文件'), false);
      }
      cb(null, true);
    },
    // 限制文件大小为5MB
    limits: {
      fileSize: 5 * 1024 * 1024
    }
  }),
)
  1. 确认路由匹配:确保React端请求URL与后端路由完全一致(含全局前缀如/api)

内容的提问来源于stack exchange,提问作者lumgaashi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 17:08:22