bigywater.tech
Spring · 47

文件上传:multipart 解析与大小限制

上传文件为什么报 413?1MB 的限制到底是单个文件还是整个请求?为什么一个只有 51 个小字段的表单也会 413?getOriginalFilename 能不能直接拿来存盘?这篇在 Spring Boot 4.0.8 的内嵌 Tomcat 11 上用真实的 multipart 请求逐个验证。

1MB + 1 字节
多 1 字节就 413,正好 1,048,576 字节能过
51
默认 multipart 部分个数上限是 50,第 51 个就 413,请求总共不到 1 KB
../../etc/passwd
getOriginalFilename() 原样返回的文件名

MultipartFile 的各种形态

实测 · 本机执行输出
  100 KB 文件 + 文本字段 note → 200 name=a.bin size=100000 read=100000 note=hello
  同名多个文件 files(3 个) → 200 files=3 sizes=[10, 20, 30]
  文件名含路径 ../../etc/passwd → 200 originalFilename=[../../etc/passwd]
  文件名含 Windows 路径 C:\\dir\\a.txt → 200 originalFilename=[C:\dir\a.txt]
  没带 file 部分(required=false) → 200 file=null
  带了 file 但 0 字节 → 200 file=empty.txt:0(isEmpty)
  带了 file 部分但文件名为空 → 200 file=:3
  没带 file(required=true) → 400 [MissingServletRequestPartException] Required part 'file' is not present.

文件名不会被清洗;0 字节文件不是 null;缺失的 file 部分按 required 决定。

默认限制

实测 · 本机执行输出
  文件 1,000,000 字节(1MB 以内) → 200 name=a size=1000000 read=1000000 note=null
  文件 1,048,576 字节(正好 1MB) → 200 name=a size=1048576 read=1048576 note=null
  文件 1,048,577 字节(超 1 字节) → 413  [Connection: close, Content-Length: 0]
  文件 2 MB → 413  [Connection: close, Content-Length: 0]
  3 个文件各 900 KB,共 2.7 MB(单个都没超 1MB) → 200 files=3 sizes=[900000, 900000, 900000]
  12 个文件各 900 KB,共 10.8 MB(总量超 10MB) → 413  [Connection: close, Content-Length: 0](客户端还没发完,连接已被服务端关闭:BrokenPipeError)

单个文件 1MB,整个请求 10MB;413 响应体为空。

调大

实测 · 本机执行输出
  文件 3 MB → 200 name=a size=3000000 read=3000000 note=null
  文件 6 MB(超 max-file-size=5MB) → 413  [Connection: close, Content-Length: 0]
  3 个文件各 4MB 共 12MB(总量超 max-request-size=10MB) → 413  [Connection: close, Content-Length: 0](客户端还没发完,连接已被服务端关闭:BrokenPipeError)

两个上限各管各的。

不限制

实测 · 本机执行输出
  限制 -1(不限制):50 MB 文件 → 200 name=big size=50000000 read=50000000 note=null

-1 可以关掉限制,但要自己守住资源。

关掉 multipart

实测 · 本机执行输出
  不是 multipart 的普通表单请求发到上传接口 → 400 [MissingServletRequestParameterException] Required request parameter 'note' for method parameter type String is not present
  multipart.enabled=false 时发 multipart → 400 [MultipartException] Failed to parse multipart servlet request

enabled=false 之后上传接口不可用。

部分个数

实测 · 本机执行输出
  10 个只有 1 字节的文本字段(整个请求不到 1 KB) → 200 parts=10
  50 个只有 1 字节的文本字段(整个请求不到 1 KB) → 200 parts=50
  51 个只有 1 字节的文本字段(整个请求不到 1 KB) → 413  [Connection: close, Content-Length: 0]
  100 个只有 1 字节的文本字段(整个请求不到 1 KB) → 413  [Connection: close, Content-Length: 0]
  51 个部分,其中 1 个是文件 → 413  [Connection: close, Content-Length: 0]

默认 50 个部分,文本字段也算。

改部分个数

实测 · 本机执行输出
  50 个文本字段 → 200 parts=50
  200 个文本字段 → 200 parts=200
  201 个文本字段 → 413  [Connection: close, Content-Length: 0]

由 server.tomcat.max-part-count 控制。

分步演示

文件上传
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动。用 Spring Boot 4.0.8 的 SpringApplication(spring-boot-starter-webmvc,内嵌 Tomcat 11.0.24);请求由 Python 的 http.client 手工拼 multipart/form-data 并分块发送,超限时客户端在发送过程中读取 413 响应;文件内容是重复的单字节
    • 没有验证:file-size-threshold 和临时文件落盘位置(spring.servlet.multipart.location);Tomcat 的 maxSwallowSize 对超限请求的影响;StandardServletMultipartResolver 之外的解析器;流式上传(不经过 MultipartFile);transferTo 的路径行为;WebFlux 的 FilePart;上传进度与断点续传
    京ICP备2026067502号-1京公网安备11010502063487号