SpringBoot使用webxml后上传文件异常multipart-form is disabled

SpringBoot使用webxml后上传文件异常multipart-form is disabled

今天遇到了上传文件的一个问题,项目环境是Springboot+JSP。因为使用了公司内部的框架,所以也需要使用web.xml。

一、表单+后台处理代码

<form method="post" action="<c:url value="/student/commit"/>" enctype="multipart/form-data">
    <input type="text" name="name"/>
    <input type="file" name="file"/>
    <input type="submit"/>
</form>

@RequestMapping(value = "/commit", method = RequestMethod.POST)
public String commit(@RequestParam("name") String name,
                     @RequestParam("file") MultipartFile file) {

    logger.info(name);
    try {
        logger.info(file.getInputStream().toString());
    } catch (IOException e) {
        e.printStackTrace();
    }

    return "/student/commit";
}

一个非常简单的文件上传,错误里的 no multi-part configuration has been provided 是什么意思,未提供文件上传的配置?

可我分明在 spring-web.xml 里加入了相应的配置。

我再仔细看,Could not parse multipart servlet request,没有 servlet 的配置?

异常堆栈:

2019-08-20 15:56:44.084 ERROR 5104 --- [esin-port-80-48] o.s.b.w.servlet.support.ErrorPageFilter  : Forwarding to error page from request [/admin/student/import.do] due to exception [Failed to parse multipart servlet request; nested exception is javax.servlet.ServletException: multipart-form is disabled; check @MultipartConfig annotation on 'geli-springboot'.]

org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request; nested exception is javax.servlet.ServletException: multipart-form is disabled; check @MultipartConfig annotation on 'geli-springboot'.
	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.handleParseFailure(StandardMultipartHttpServletRequest.java:123) ~[spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:114) ~[spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.<init>(StandardMultipartHttpServletRequest.java:87) ~[spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at org.springframework.web.multipart.support.StandardServletMultipartResolver.resolveMultipart(StandardServletMultipartResolver.java:87) ~[spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.checkMultipart(DispatcherServlet.java:1176) ~[spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1011) ~[spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) ~[spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) ~[spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908) ~[spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:159) ~[javaee-16.jar:na]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882) ~[spring-webmvc-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:97) ~[javaee-16.jar:na]
	at com.caucho.server.dispatch.ServletFilterChain.doFilter(ServletFilterChain.java:109) ~[resin.jar:4.0.62]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.22.jar:9.0.22]
	at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:89) [resin.jar:4.0.62]
	at org.gelivable.web.EnvFilter.doFilter(EnvFilter.java:83) [geli-spring-boot-3.0.1.jar:na]
	at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:89) [resin.jar:4.0.62]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) [spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:89) [resin.jar:4.0.62]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) ~[spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) [spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:89) [resin.jar:4.0.62]
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) ~[spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) [spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:89) [resin.jar:4.0.62]
	at org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:128) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
	at org.springframework.boot.web.servlet.support.ErrorPageFilter.access$000(ErrorPageFilter.java:66) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
	at org.springframework.boot.web.servlet.support.ErrorPageFilter$1.doFilterInternal(ErrorPageFilter.java:103) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) [spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:121) [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
	at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:89) [resin.jar:4.0.62]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) [spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118) [spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:89) [resin.jar:4.0.62]
	at org.gelivable.web.AbstractAuthFilter.doFilter(AbstractAuthFilter.java:56) [geli-spring-boot-3.0.1.jar:na]
	at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:89) [resin.jar:4.0.62]
	at org.gelivable.web.EnvFilter.doFilter(EnvFilter.java:83) [geli-spring-boot-3.0.1.jar:na]
	at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:89) [resin.jar:4.0.62]
	at com.caucho.server.webapp.WebAppFilterChain.doFilter(WebAppFilterChain.java:156) [resin.jar:4.0.62]
	at com.caucho.server.webapp.AccessLogFilterChain.doFilter(AccessLogFilterChain.java:95) [resin.jar:4.0.62]
	at com.caucho.server.dispatch.ServletInvocation.service(ServletInvocation.java:314) [resin.jar:4.0.62]
	at com.caucho.server.http.HttpRequest.handleRequest(HttpRequest.java:843) [resin.jar:4.0.62]
	at com.caucho.network.listen.TcpSocketLink.dispatchRequest(TcpSocketLink.java:1393) [resin.jar:4.0.62]
	at com.caucho.network.listen.TcpSocketLink.handleRequest(TcpSocketLink.java:1349) [resin.jar:4.0.62]
	at com.caucho.network.listen.TcpSocketLink.handleRequestsImpl(TcpSocketLink.java:1333) [resin.jar:4.0.62]
	at com.caucho.network.listen.TcpSocketLink.handleRequests(TcpSocketLink.java:1241) [resin.jar:4.0.62]
	at com.caucho.network.listen.TcpSocketLink.handleAcceptTaskImpl(TcpSocketLink.java:1037) [resin.jar:4.0.62]
	at com.caucho.network.listen.ConnectionTask.runThread(ConnectionTask.java:117) [resin.jar:4.0.62]
	at com.caucho.network.listen.ConnectionTask.run(ConnectionTask.java:93) [resin.jar:4.0.62]
	at com.caucho.network.listen.SocketLinkThreadLauncher.handleTasks(SocketLinkThreadLauncher.java:175) [resin.jar:4.0.62]
	at com.caucho.network.listen.TcpSocketAcceptThread.run(TcpSocketAcceptThread.java:61) [resin.jar:4.0.62]
	at com.caucho.env.thread2.ResinThread2.runTasks(ResinThread2.java:173) [resin.jar:4.0.62]
	at com.caucho.env.thread2.ResinThread2.run(ResinThread2.java:118) [resin.jar:4.0.62]
Caused by: javax.servlet.ServletException: multipart-form is disabled; check @MultipartConfig annotation on 'geli-springboot'.
	at com.caucho.server.http.AbstractCauchoRequest.getParts(AbstractCauchoRequest.java:246) ~[resin.jar:4.0.62]
	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:94) ~[spring-web-5.1.9.RELEASE.jar:5.1.9.RELEASE]
	... 52 common frames omitted

二、谜题之解

我这才意识到,SpringMVC 是依赖 Servlet 的,虽然 SpringMVC 能识别 Servlet,但它底层的 Servlet 也许还不能识别文件上传。

SpringMVC 官网的 refference 去看文档,果然,我发现了一些猫腻:

Once Servlet 3.0 multipart parsing has been enabled in one of the above mentioned ways you can add the StandardServletMultipartResolver to your Spring configuration

在配置 SpringMVC 的 StandardServletMultipartResolver 之前必须先配置 Servlet,官网提供的方案有:

  • 1.mark the DispatcherServlet with a “multipart-config” section in web.xml(在 web.xml 文件中的 DispatcherServlet 元素里,加上一个 multipart-config)
  • 2.with a javax.servlet.MultipartConfigElement in programmatic Servlet registration
  • 3.in case of a custom Servlet class possibly with a javax.servlet.annotation.MultipartConfig annotation on your Servlet class

我选择了第一种方案,打开 webapp/WEB-INFO 目录下的 web.xml,往 DispatcherServlet 里加了一行

<multipart-config/>

再运行项目,就可以正常地上传文件了。

web.xml 全部内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app
        version="3.0"
        xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
		http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <display-name>geli-springboot</display-name>

    <servlet>
        <servlet-name>geli-springboot</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:application-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <!--让Servlet支持文件上传-->
        <multipart-config/>
    </servlet>

    <servlet-mapping>
        <servlet-name>geli-springboot</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>/admin/login.jsp</welcome-file>
    </welcome-file-list>

</web-app>

参考文档:

https://www.iloveqyc.com/2016/06/03/springmvc-upload-file-fail-servlet/

https://juejin.im/post/5a730f895188257a6d634f0a#heading-6

Spring揭秘--寻找遗失的web.xml

https://www.cnkirito.moe/servlet-explore/

-------------已经触及底线 感谢您的阅读-------------
原文地址:https://www.cnblogs.com/cnsyear/p/12777888.html