springboot文件上传,文件过大引发异常SizeLimitExceededException——思考与解决办法(Debug分析自动装配)

今天在打代码的时候遇到了一个“上传文件过大”的报错,本来是一个小问题,随手百度了一下想要解决。
但是遇到了一种授人以渔的方法。在学习过后,很是激动,在学习过程中,将之前学的不是很透彻的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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值