React/Node上传MultipartFile至Spring控制器报错排查
核心问题:手动设置Content-Type破坏了multipart请求的边界信息
你遇到的两个报错本质都是Node端请求头的设置不符合Spring MultipartResolver的规范要求:
不传递
Content-Type时Spring提示参数缺失:
当你不手动设置Content-Type时,fetch没有自动标识这是一个multipart类型的请求,Spring的MultipartResolver无法识别请求体中的文件参数结构,因此判定file参数不存在。传递
Content-Type: multipart/form-data时提示边界缺失:
标准的multipart请求必须在Content-Type中包含自动生成的boundary(请求体分隔符),格式类似multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW。手动设置Content-Type会覆盖fetch自动生成的带boundary的请求头,导致Spring无法解析请求体的分隔逻辑,从而抛出边界缺失的错误。
为什么Dropwizard能正常工作?
Dropwizard基于JAX-RS规范实现,其@FormDataParam对应的multipart解析器对请求格式的容错性更高,即使请求头不严格符合RFC规范,也能尝试解析请求体中的文件数据。而Spring的MultipartResolver(默认是StandardServletMultipartResolver或CommonsMultipartResolver)对请求格式的要求更严格,必须完全符合multipart请求的规范标准。
具体修复步骤
1. 移除手动设置的Content-Type头
让fetch自动根据FormData对象生成包含boundary的正确Content-Type:
fetch(serviceCallRequestData.url, { method: serviceCallRequestData.requestObject.method, // 过滤掉手动设置的Content-Type,让fetch自动生成 headers: Object.fromEntries( Object.entries(serviceCallRequestData.requestObject.headers).filter(([key]) => key !== 'Content-Type') ), body: formData })
2. 优化FormData的文件追加方式
追加文件时带上文件名(可选,但能让Spring更准确识别文件元信息):
// 直接使用file的原始buffer,并指定文件名 formData.append('file', file.data, file.name);
3. (可选)Spring控制器明确指定接收类型
在Spring的@RequestMapping中添加consumes属性,明确声明接口接收multipart类型请求:
@RequestMapping( value = "/data/upload", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE ) public @ResponseBody void uploadData(@RequestParam("file") MultipartFile file) throws IOException { logger.info("Received File for Ingestion"); dataUploadService.processData(file.getInputStream()); }
经过以上调整,Node端发送的请求会完全符合Spring的multipart解析要求,就能正常完成文件上传了。
内容的提问来源于stack exchange,提问作者User_Targaryen

