今天在打代码的时候遇到了一个“上传文件过大”的报错,本来是一个小问题,随手百度了一下想要解决。
但是遇到了一种授人以渔的方法。在学习过后,很是激动,在学习过程中,将之前学的不是很透彻的SpringBoot自动装配机制和不是很熟练的Debug调试都学习串联了起来,有一种恍然大悟的感觉,遂将调试过程记录下来,以免因不熟忘记这种学习思路。
首先是报错信息
2021-04-21 12:01:02.305 ERROR 2896 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.multipart.MaxUploadSizeExceededException: Maximum upload size exceeded; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException: the request was rejected because its size (70393426) exceeds the configured maximum (10485760)] with root cause
org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException: the request was rejected because its size (70393426) exceeds the configured maximum (10485760)
at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.init(FileItemIteratorImpl.java:146) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.getMultiPartStream(FileItemIteratorImpl.java:190) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.findNextItem(FileItemIteratorImpl.java:209) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.<init>(FileItemIteratorImpl.java:127) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.tomcat.util.http.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:256) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.tomcat.util.http.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:280) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.connector.Request.parseParts(Request.java:2922) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.connector.Request.getParts(Request.java:2824) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.connector.RequestFacade.getParts(RequestFacade.java:1098) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:95) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.<init>(StandardMultipartHttpServletRequest.java:88) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.multipart.support.StandardServletMultipartResolver.resolveMultipart(StandardServletMultipartResolver.java:87) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.DispatcherServlet.checkMultipart(DispatcherServlet.java:1198) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1032) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.3.5.jar:5.3.5]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:652) ~[tomcat-embed-core-9.0.44.jar:4.0.FR]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.5.jar:5.3.5]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.44.jar:4.0.FR]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.
...
点击报错信息中最靠上方的类(FileItemIteratorImpl.java:146),会看到关键字sizeMax,然后在这一行打上一个断点,再Debug运行,可以看到sizeMax是从FileUploadBase中传过来的,
然后我们进入FileUploadBase类寻找sizeMax,可以找到sizeMax的get、set方法。在set方法处添加断点然后Debug,按f8下一步,查看sizeMax的值是从哪里传过来的。
通过debug的控制台的变量数据显示,可以看到这个sizeMax的值是从mce中的maxRequestSize里传过来的,点击mce的getMaxRequestSize方法,进入到MultipartConfigElement类中
发现其中没有set方法,设置值只能通过构造方法,所以我们给三个构造方法都打上断点,下方是源代码,
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package javax.servlet;
import javax.servlet.annotation.MultipartConfig;
/**
* @since Servlet 3.0
* TODO SERVLET3 - Add comments
*/
public class MultipartConfigElement {
private final String location;// = "";
private final long maxFileSize;// = -1;
private final long maxRequestSize;// = -1;
private final int fileSizeThreshold;// = 0;
public MultipartConfigElement(String location) {
// Keep empty string default if location is null
if (location != null) {
this.location = location;
} else {
this.location = "";
}
this.maxFileSize = -1;
this.maxRequestSize = -1;
this.fileSizeThreshold = 0;
}
public MultipartConfigElement(String location, long maxFileSize,
long maxRequestSize, int fileSizeThreshold) {
// Keep empty string default if location is null
if (location != null) {
this.location = location;
} else {
this.location = "";
}
this.maxFileSize = maxFileSize;
this.maxRequestSize = maxRequestSize;
// Avoid threshold values of less than zero as they cause trigger NPEs
// in the Commons FileUpload port for fields that have no data.
if (fileSizeThreshold > 0) {
this.fileSizeThreshold = fileSizeThreshold;
} else {
this.fileSizeThreshold = 0;
}
}
public MultipartConfigElement(MultipartConfig annotation) {
location = annotation.location();
maxFileSize = annotation.maxFileSize();
maxRequestSize = annotation.maxRequestSize();
fileSizeThreshold = annotation.fileSizeThreshold();
}
public String getLocation() {
return location;
}
public long getMaxFileSize() {
return maxFileSize;
}
public long getMaxRequestSize() {
return maxRequestSize;
}
public int getFileSizeThreshold() {
return fileSizeThreshold;
}
}
启动Debug之后,通过不断的f8我们可以看到,确实是通过构造方法传入了值,然后我们继续下一步。
来到了调用MultipartConfigElement构造方法的类——MultipartConfigFactory,这中Factory的类就很熟悉了,是使用了工厂模式
再下一步,到了MultipartProperties类,这是SpringBoot自动装配的Properties类,
下一步,我们来到了MultipartAutoConfiguration,这就是正宗的自动装配的类了。
看上方的注解,@ConditionalOnMissingBean,如果Spring中没有MultipartConfigElement的Bean,则自动执行。所以我只需要自己写一个返回MultipartConfigElement的方法,在其中更改sizeMax,然后再注入bean,就可以了。
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setMaxFileSize(DataSize.ofBytes(1048576*200));//1048567是1Mb
factory.setMaxRequestSize(DataSize.ofBytes(1048576*200));
return factory.createMultipartConfig();
}
或者直接在applicationProperties.xml中进行修改
spring.servlet.multipart.max-request-size=209715201
spring.servlet.multipart.max-file-size=209715202
亲测可行,注意setMaxFileSize只支持DataSize格式的数据
public void setMaxFileSize(DataSize maxFileSize)
可以通过DataSize的静态方法获得DataSize的对象
public static DataSize ofBytes(long bytes) {
return new DataSize(bytes);
}
最后,感谢我参考的博主,此处挂上原文链接:springboot文件上传,文件过大导致异常the request was rejected because its size (170982031) exceeds the configured