Spring Boot multipart/form-data上传时MultipartFile始终为空如何解决
问题原因解析
你遇到的问题根源是Servlet请求的输入流只能被读取一次,结合你的自定义过滤器逻辑,具体触发路径如下:
- 你的
BufferedRequestWrapper构造逻辑中,只要不是application/x-www-form-urlencoded类型的请求,都会直接读取原始HttpServletRequest的InputStream,把请求体全部读到字节缓存里,原始请求的输入流就被消费完了。 - Spring MVC默认处理
multipart/form-data请求时,会由MultipartResolver读取请求输入流解析文件参数,这时候拿到的已经是空流,自然解析不到file参数,所以会返回null。
你提前调用request.getParts()能生效的原因是:
Servlet容器(比如Tomcat、Undertow)的getParts()方法原生负责解析multipart请求,调用后会把解析得到的所有Part对象(包括文件、普通表单字段)缓存到请求对象中,后续不管是读取输入流还是再次调用getParts(),都会直接使用缓存的解析结果,不会再去读取原始输入流。所以你先触发解析缓存后,后续你自己读输入流做日志、Spring再解析参数都能正常获取内容。
更优实现方案
你现在手动调用getParts()的方案可以正常运行,但还有更优雅、维护成本更低的实现方式:
- 方案1:直接使用Spring官方提供的
ContentCachingRequestWrapper替代自定义的BufferedRequestWrapper
Spring自带的这个请求包装类已经兼容了multipart请求场景,它不会提前消费multipart请求的输入流,会等Spring的MultipartResolver完成文件解析后再缓存请求内容,完全不需要手动触发getParts()调用,也不会影响文件参数解析。 - 方案2:调整过滤器执行顺序
把你的日志过滤器的执行顺序调整到Spring的MultipartFilter之后,MultipartFilter会提前完成multipart请求的解析,把请求包装成MultipartHttpServletRequest往后传递,这时候你再读取输入流做日志,也不会影响已经解析好的MultipartFile参数。 - 方案3:修改自定义包装类逻辑
如果你一定要保留自定义的BufferedRequestWrapper,可以调整构造逻辑:碰到multipart/form-data类型的请求时,不直接读取输入流,而是通过getParts()获取内容做日志,避免提前消费原始流。
内容的提问来源于stack exchange,提问作者Jonas Pedersen
相关产品推荐
相关产品推荐

