springboot文件上传

以前通过spring使用文件上传时,使用的是commons-io、commons-fileupload组件整合来实现的,转移到springboot之后,也就没关注这块,直接取使用了,最近发现一些配置的影响,代码处理的逻辑与预期的有很大的处理,于是简单的了解了具体的实现方式。

首先我们要知道,在springboot中,处理文件上传和spring中是一样的,我们在进行请求提交时,在DispatcherServlet中会对该请求进行校验:

	protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
		if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
			if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
				logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +
						"this typically results from an additional MultipartFilter in web.xml");
			}
			else if (hasMultipartException(request)) {
				logger.debug("Multipart resolution previously failed for current request - " +
						"skipping re-resolution for undisturbed error rendering");
			}
			else {
				try {
					return this.multipartResolver.resolveMultipart(request);
				}
				catch (MultipartException ex) {
					if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) {
						logger.debug("Multipart resolution failed for error dispatch", ex);
						// Keep processing error dispatch with regular request handle below
					}
					else {
						throw ex;
					}
				}
			}
		}
		// If not returned before: return original request.
		return request;
	}

核心还是multipartResolver这个,所有逻辑都是通过它来完成。

在 spring-boot-autoconfigure这个jar中找到spring.factories,所有的自动配置都在这里完成

org.springframework.boot.autoconfigure.EnableAutoConfiguration

关于上面说到的MultipartResolver,主要是通过org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration这个类配置来完成。

@Configuration
@ConditionalOnClass({ Servlet.class, StandardServletMultipartResolver.class,
		MultipartConfigElement.class })
@ConditionalOnProperty(prefix = "spring.http.multipart", name = "enabled",
		matchIfMissing = true)
@EnableConfigurationProperties(MultipartProperties.class)
public class MultipartAutoConfiguration {

	private final MultipartProperties multipartProperties;

	public MultipartAutoConfiguration(MultipartProperties multipartProperties) {
		this.multipartProperties = multipartProperties;
	}

	@Bean
	@ConditionalOnMissingBean({ MultipartConfigElement.class,
			CommonsMultipartResolver.class })
	public MultipartConfigElement multipartConfigElement() {
		return this.multipartProperties.createMultipartConfig();
	}

	@Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
	@ConditionalOnMissingBean(MultipartResolver.class)
	public StandardServletMultipartResolver multipartResolver() {
		StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
		multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
		return multipartResolver;
	}

}

其实可以看到,默认情况下,系统使用了StandardServletMultipartResolver作为MultipartResolver默认实现,里面具体做了什么,跟着上面的方法resolveMultipart进去看下就知道,其实主要的实现逻辑是解析HttpServletRequest,讲其转换成另一个子类MultipartHttpServletRequest,主要是针对Servlet3的特性对附件和请求参数进行处理,全部封装到这个参数之中。因此我们在发送请求的时候,可以直接通过MultipartHttpServletRequest来接收请求进行处理。

其实到这里,springboot的文件上传基本就这个逻辑,但是我们可以看到,spring中对MultipartResolver接口还有另外一个实现类CommonsMultipartResolver,这个就是最开始说到的,因此在使用它时,我们需要将commons的两个包导入进来。然而说到的问题就是在使用时,同样在controller中用MultipartHttpServletRequest处理请求,但是无论如何,获取到的请求都取不到附件。通过调试也没发现任何问题。

在此之前我们需要分析一下,我们不管使用上面的那种方式,都是对request处理,而request一旦被消费,那么里面的附件也就没了!

比如通过Servlet3处理附件,主要是通过Requets->getParts()获取相关参数,然后解析进行下一步封装。而一旦这样做了后,apache commons处理请求时,就再也获取不到请求了。所以上面的问题会不会是在CommonsMultipartResolver处理前,请求已经被消费了?

目前通过断点也没发现具体是哪里导致这个问题,但是通过添加@EnableWebMvc或者设置 spring.http.multipart.enabled=false则可以正常,也就是不处理MultipartAutoConfiguration。

下面说几个需要注意的点:

1、CommonsMultipartResolver + @EnableWebMvc可以正常使用

2、CommonsMultipartResolver + spring.http.multipart.enabled=false 也是正常的

3、如果使用CommonsMultipartResolver在配置文件中的spring.http.multipart.*都将无效

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值