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

React/NestJS多文件上传报400错误:无法同时上传CV与头像

解决NestJS+React同时上传两个文件时的400 Bad Request错误

你遇到的核心问题是NestJS中不能同时使用多个FileInterceptor。FileInterceptor专门用于处理单个文件上传,叠加使用时第二个拦截器会覆盖第一个的请求解析逻辑,导致其中一个文件无法被正确捕获,触发后端校验失败返回400错误。

后端NestJS代码修复

使用FieldsInterceptor替代多个FileInterceptor,它支持同时处理多个不同字段的文件上传:

  1. 先导入所需的拦截器和装饰器:
import { FieldsInterceptor, UploadedFiles } from '@nestjs/platform-express';
  1. 修改接口的拦截器配置与参数获取逻辑:
@Post('signup')
@UseInterceptors(FieldsInterceptor([
  { name: 'cv', maxCount: 1 },
  { name: 'avatarImage', maxCount: 1 },
]))
async signUp(
  @Body() signupDto: SignupDto,
  @UploadedFiles() files: { cv?: Express.Multer.File[], avatarImage?: Express.Multer.File[] },
): Promise<any> {
  // 提取单个文件(maxCount设为1,数组第一个元素即为目标文件)
  const cv = files.cv?.[0];
  const avatarImage = files.avatarImage?.[0];

  if (!cv) {
    throw new Error('CV file is required.');
  }
  signupDto.cvFileName = cv.originalname;
  signupDto.cvFileBuffer = cv.buffer;

  signupDto.cv = await this.fileUploadService.uploadFileCv(
    signupDto.cvFileName,
    signupDto.cvFileBuffer,
  );

  if (!avatarImage) {
    throw new Error('Avatar Image is required');
  }

  signupDto.avatarImageName = avatarImage.originalname;
  signupDto.avatarImageFileBuffer = avatarImage.buffer;

  signupDto.avatarImage = await this.fileUploadService.uploadImage(
    signupDto.avatarImageName,
    signupDto.avatarImageFileBuffer,
  );
  
  return this.userService.createUser(signupDto);
}

前端React代码优化

前端逻辑整体无大问题,可做两处优化避免潜在bug:

  • 仅在存在有效文件时才添加到FormData,避免传入空字符串
  • 校验文件类型,确保是合法的File对象

修改后的handleRegistration:

const handleRegistration = async (event: FormEvent<HTMLFormElement>) => {
  event.preventDefault();

  const formData = new FormData();
  // 仅添加有效文件
  if (selectedCvFile.length > 0 && selectedCvFile[0] instanceof File) {
    formData.append("cv", selectedCvFile[0]);
  }
  if (selectedImagesForAvatar.length > 0 && selectedImagesForAvatar[0] instanceof File) {
    formData.append("avatarImage", selectedImagesForAvatar[0]);
  }

  // 仅添加有值的表单字段
  Object.entries(formState).forEach(([key, value]) => {
    if (value) {
      formData.append(key, value);
    }
  });

  dispatch({ type: "CLEAR_ALERTS" });
  try {
    const response = await fetch("http://localhost:3000/auth/signup", {
      method: "POST",
      body: formData,
    });

    const data = await response.json();

    if (response.ok) {
      dispatch({
        type: "SHOW_SUCCESS",
        payload: `You have registered correctly !`,
      });
      console.log(data);
      localStorage.setItem("token", data.accessToken);

    } else {
      dispatch({ type: "SHOW_ERROR", payload: response.statusText });
      console.error("Registration failed:", response.statusText, response);
    }
  } catch (error) {
    console.error("Error during registration:", error);
  }
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 15:01:20