遇到的问题
springmvc图片解析的bean设置
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
p:defaultEncoding="UTF-8">
<!-- 指定所上传文件的总大小不能超过10M。注意maxUploadSize属性的限制不是针对单个文件,而是所有文件的容量之和 -->
<property name="maxUploadSize" value="10482760" />
<!-- 设置在文件上传时允许写到内存中的最大值,以字节为单位计算,默认是10240 -->
<property name="maxInMemorySize" value="10240" />
</bean>
<!-- SpringMVC在超出上传文件限制时,会抛出org.springframework.web.multipart.MaxUploadSizeExceededException -->
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!-- 遇到MaxUploadSizeExceededException异常时,自动跳转到/error/exceedUpload.jsp页面 -->
<prop
key="org.springframework.web.multipart.MaxUploadSizeExceededException">error/exceedUpload
</prop>
</props>
</property>
</bean>
CommonsMultipartResolver
- 根据bean的配置,CommonsMultipartResolver此类将上传的图片或者文件生成一个临时文件,临时文件分为内存中的临时文件,硬盘中的临时文件,根据bean的属性放到内存或者硬盘。在使用时,上传图片都是从硬盘的临时文件读取,所以一直上传不成功,只需要将bean的maxInMemorySize属性设置小一点,CommonsMultipartResolver生成的临时文件就能够放到硬盘里面。
问题分析找到bean设置对应源码处
- springmvc处理request的核心DispatcherServlet.doDispatch方法里面调用checkMultipart,此方法中要请求是post,而且context以multipart/开头的才能被解析成CommonsMultipart类型,否则当普通类型处理。
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
//重点要分析的
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
。。。。。。
}
else if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) instanceof MultipartException) {
。。。。。。。
}
else {
//将request 包装成DefaultMultipartHttpServletRequest这样才后面才
//RequestParamMethodArgumentResolver.resolveName的
//Assert.notNull(Assert.java:112)才会根据request的类型通过
//assertIsMultipartRequest只检查是conentTtype是否以multi/part开头
return this.multipartResolver.resolveMultipart(request);
}
}
// If not returned before: return original request.
return request;
}
public boolean isMultipart(HttpServletRequest request) {
return (request != null && ServletFileUpload.isMultipartContent(request));
}
public static final boolean isMultipartContent(
HttpServletRequest request) {
if (!"post".equals(request.getMethod().toLowerCase())) {
return false;
}
String contentType = request.getContentType();
if (contentType == null) {
return false;
}
if (contentType.toLowerCase().startsWith(MULTIPART)) {
return true;
}
return false;
}
- 调用CommonsMultipartResolver.resolveMultipart方法,调用ServletFileUpload.parseRequest方法。
- 调用FileUploadBase.getIlemIterator方法
public FileItemIterator getItemIterator(RequestContext ctx) throws FileUploadException, IOException {
return new FileUploadBase.FileItemIteratorImpl(ctx);
}
FileItemIteratorImpl(RequestContext ctx) throws FileUploadException, IOException {
//这里的sizeMax就是bean里面设置的maxUploadSize
if (requestSize != -1L && requestSize > FileUploadBase.this.sizeMax) {
throw new FileUploadBase.SizeLimitExceededException(String.format("the request was rejected because its size (%s) exceeds the configured maximum (%s)", requestSize, FileUploadBase.this.sizeMax), requestSize, FileUploadBase.this.sizeMax);
}
}
- 最后用Streams.copy(...fileItem.getOutputStream(), ...)进行临时文件的生成,这里面决定了是在内存中还是在硬盘生成临时文件。
public OutputStream getOutputStream() throws IOException {
if (this.dfos == null) {
File outputFile = this.getTempFile();
//此类有isMemory方法
this.dfos = new DeferredFileOutputStream(this.sizeThreshold, outputFile);
}
return this.dfos;
}
- DeferredFileOutputStream.isInMemory方法决定是在内存中还是硬盘里面生成临时文件
public boolean isInMemory() {
//调用父类ThresholdingOutputStream
return !this.isThresholdExceeded();
}
public boolean isThresholdExceeded() {
//written是我们写入文件大小 threshold对应bean的maxInMemorySize
return this.written > (long)this.threshold;
}