表单未设enctype="multipart/form-data"导致后端收不到文件,因浏览器默认application/x-www-form-urlencoded且不自动切换;手动设置content-type会破坏boundary生成;spring boot需显式启用multipart支持,否则抛multipartexception。

表单没设 enctype="multipart/form-data",后端就根本收不到文件——不是解析失败,是压根没进 multipart 分支。
为什么浏览器不自动补全 multipart 编码
浏览器对 <form></form> 的 enctype 有严格默认值:application/x-www-form-urlencoded。哪怕你加了 <input type="file">,它也不会“聪明地”切到 multipart/form-data。漏写或写错(比如拼成 mutlipart、多空格、引号用中文)都会导致请求头里 Content-Type 不含 boundary=,Spring MVC 日志里连 MultipartResolver 的调用痕迹都没有。
常见表现:
- 前端选了文件、点了提交,后端
@RequestParam("file") MultipartFile file始终为null - Network 面板里该请求的
Content-Type是application/x-www-form-urlencoded或text/plain - Spring 日志里出现
MultipartException: Current request is not a multipart request
fetch / axios 手动设置 Content-Type 就会炸
用 JS 构造上传时,FormData 对象本身已隐含 multipart/form-data 结构,浏览器会在调用 fetch() 或 xhr.send() 时自动生成合法 boundary。一旦你手动加了 headers: { 'Content-Type': 'multipart/form-data' },浏览器就会放弃自动注入 boundary,导致后端收到的请求体没有分隔符,直接解析失败。
正确做法:
-
fetch('/upload', { method: 'POST', body: formData })—— 不设Content-Type头 -
axios.post('/upload', formData)—— 同样别传headers项 - 如果必须用
axios控制超时等参数,只传{ timeout: 30000 },绝不要碰Content-Type
Spring Boot 没配 multipart 解析器也会报错
即使前端完全正确,Spring Boot 默认也不会启用 multipart 支持——它需要显式声明一个 MultipartResolver Bean,否则遇到带 boundary 的请求,会直接抛 MultipartException,提示“无法处理 parts”。
检查点:
- 确认
spring.servlet.multipart.enabled=true(Spring Boot 2.0+ 默认开启,但可能被覆盖) - 检查
application.yml是否误设了max-file-size: 0B或enabled: false - 若用老版本 Spring 或嵌入式 Tomcat,需确保
web.xml里有<multipart-config></multipart-config>块,否则 Servlet 容器层就拒绝解析
错误日志典型特征:Failed to parse multipart servlet request + java.lang.IllegalStateException: Unable to process parts,说明请求已到达容器,但底层没开 multipart 支持。
curl 测试时 -F 和 -d 绝对不能混用
调试时用 curl 模拟上传,必须用 -F 参数:curl -F "file=@test.jpg" http://localhost:8080/upload。它会自动构造带 boundary 的完整 multipart 请求体。
千万别用 -d 手动拼接:
-
-d "file=@test.jpg":只是把字符串当普通表单字段发,文件内容不会被读取 -
-d "------boundary...":手动写 boundary 几乎必然格式错,且浏览器生成的 boundary 是随机字符串,不可预测 - 结果都是后端收到乱码或空文件,且日志里看不到 multipart 解析流程
真正麻烦的是:有些框架(如旧版 Spring MVC)在未配置 MultipartResolver 时,对非 multipart 请求反而能走普通参数绑定;一加上 enctype,反而因“识别出是 multipart 却没人处理”而报错——这种反直觉现象最容易让人误判问题方向。











