Spring Boot MultipartFile如何提前校验文件大小避免全量上传
你配置的spring.servlet.multipart.max-file-size、spring.servlet.multipart.max-request-size属于Spring框架Multipart解析层的校验规则,触发时机是Web容器已经将整个请求体(包含完整上传文件)全部接收完毕,开始解析请求内Multipart内容的阶段。从你抛出的异常可以看出当前使用Undertow作为Web容器,默认配置下Undertow会先完整接收所有请求字节,才会向上层传递解析事件,因此会出现超大文件完全上传完成才抛出异常的问题。
顺带说明:你贴出的配置缺少spring前缀,正确配置路径应为spring.servlet.multipart,前缀缺失会导致该部分配置不生效,但即使配置书写正确,也无法解决全量接收后才校验的问题。
另外不要完全依赖请求头的Content-Length字段做大小判断,客户端可以随意伪造该字段值,分块传输(Transfer-Encoding: chunked)场景下该字段甚至不会存在,仅靠这个字段校验会有绕过风险。
最省资源的校验逻辑是在Web容器接收请求字节流的阶段就做大小阈值判断,累计读取的字节数超过限制时直接终止连接、抛出异常,无需继续接收剩余的文件内容。
针对Undertow容器,直接添加如下配置即可:
import io.undertow.UndertowOptions; import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class UndertowWebServerConfig { @Bean public UndertowServletWebServerFactory undertowServletWebServerFactory() { UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory(); // 10MB对应字节数为10 * 1024 * 1024 = 10485760 factory.addBuilderCustomizers(builder -> builder.setServerOption(UndertowOptions.MAX_ENTITY_SIZE, 10485760L) ); return factory; } }
如果你使用Tomcat作为Web容器,逻辑一致,对应配置Tomcat Connector的maxPostSize参数即可实现字节接收阶段的阈值校验。
可以新增一层Web过滤器,对带合法Content-Length头的上传请求做快速失败判断,直接拦截明确超过大小限制的请求,减少不必要的流读取:
import jakarta.servlet.*; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import java.io.IOException; @Component public class MultipartUploadFilter implements Filter { private static final long MAX_ALLOWED_SIZE = 10 * 1024 * 1024; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String contentType = req.getContentType(); if (contentType != null && contentType.startsWith("multipart/")) { long requestContentLength = req.getContentLengthLong(); if (requestContentLength > MAX_ALLOWED_SIZE) { ((HttpServletResponse) response).sendError(HttpStatus.PAYLOAD_TOO_LARGE.value(), "上传文件大小不得超过10MB"); return; } } chain.doFilter(request, response); } }
- 过滤器层的
Content-Length校验仅作为快速拦截手段,不能替代容器层的流读取校验,避免被恶意构造的请求绕过。 - 容器层校验配置完成后,原有的Spring Multipart大小配置可以保留,两层校验不冲突:容器层负责请求总大小的提前拦截,Spring层负责解析阶段的细粒度校验(比如多文件上传时单个文件大小超限的场景)。
- 配置生效后,超大文件上传时,容器读取到阈值大小的字节后就会直接终止请求,不会继续接收剩余内容,大幅降低服务器带宽和内存消耗。
内容的提问来源于stack exchange,提问作者Harshana

