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