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要求,同时后端异常处理逻辑掩盖了真实错误:
- Multer仅解析
multipart/form-data类型请求,且要求表单字段名与FileInterceptor('file')中的参数完全一致 - React端可能未使用
FormData构造请求体,或字段名不匹配 - 后端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)优化
- 修复异常捕获逻辑:不要吞掉错误,打印错误信息便于排查
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', }), ); } }
- 添加文件校验(可选):提前拦截非法文件,避免后续错误
@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 } }), )
- 确认路由匹配:确保React端请求URL与后端路由完全一致(含全局前缀如
/api)
内容的提问来源于stack exchange,提问作者lumgaashi
相关产品推荐
相关产品推荐

