package com.xiaopeng.automaticPilot.config;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Configuration;
|
|
import javax.servlet.*;
|
import javax.servlet.http.HttpServletRequest;
|
import java.io.IOException;
|
|
|
/**
|
* 修复 multipart 文件上传被 ReplaceRequestFilter 破坏的问题。
|
* <p>
|
* ReplaceRequestFilter(来自 request-wrapper jar,logan-starter-core 传递依赖)
|
* 会读取 request 的 InputStream 来包装请求体,导致后续 Spring 解析 multipart 时
|
* 抛出 "Stream closed" 异常。
|
* <p>
|
* 解决方案:注册一个高优先级 Filter,对 multipart 请求提前触发 Servlet 容器的
|
* multipart 解析(通过调用 request.getParts()),使文件数据被缓存到临时文件中。
|
* 这样即使后续 ReplaceRequestFilter 消费了 InputStream,multipart 数据仍然可用。
|
*/
|
@Slf4j
|
@Configuration
|
public class MultipartFilterConfig {
|
|
private static final String MULTIPART = "multipart/";
|
@Bean
|
public FilterRegistrationBean<Filter> earlyMultipartParsingFilter() {
|
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
|
registration.setFilter(new EarlyMultipartParsingFilter());
|
registration.addUrlPatterns("/*");
|
// 确保在 ReplaceRequestFilter 之前执行
|
registration.setOrder(Integer.MIN_VALUE + 10);
|
registration.setName("earlyMultipartParsingFilter");
|
return registration;
|
}
|
/**
|
* 在 filter 链最前面对 multipart 请求提前解析,
|
* 让 Tomcat 将文件数据缓存到磁盘,避免后续 filter 消费 InputStream 导致 Stream closed。
|
*/
|
static class EarlyMultipartParsingFilter implements Filter {
|
@Override
|
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
throws IOException, ServletException {
|
if (request instanceof HttpServletRequest) {
|
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
String contentType = httpRequest.getContentType();
|
if (contentType != null && contentType.toLowerCase().startsWith(MULTIPART)) {
|
// 提前触发 multipart 解析,Tomcat 会将 parts 缓存
|
try {
|
httpRequest.getParts();
|
} catch (Exception e) {
|
log.warn("Early multipart parsing failed, will retry later: {}", e.getMessage());
|
}
|
}
|
}
|
chain.doFilter(request, response);
|
}
|
}
|
|
}
|