springMVC:为MultipartFilte配置了上传文件解析器,报错或不能使用

一、问题描述
为支持restful风格请求,并且应对可能上传文件的情况,需要在配置hiddenHttpMethodFilter过滤器之前配置MultipartFilter。目的是让MultipartFilter过滤器先将带文件上传的请求,进行解析。以便hiddenHttpMethodFilter可以取到”_method”参数,转化为相应的http动作。
既然multipartFilter要进行上传文件的解析,那么必然需要MutipartResolver,那么问题发生了!

二、报错:Unable to process parts as no multi-part configuration has been provided
配置如下

web.xml

<filter>
        <filter-name>MultipartFilter</filter-name>
        <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
        <init-param>
            <param-name>multipartResolverBeanName</param-name>
            <param-value>multipartResolver</param-value>
        </init-param>
</filter>

springmvc.xml DispatcherServlert的上下文文件 
我们使用了commons-fileupload-1.3.1.jar提供的CommonsMultipartResolver解析器

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
       <property name="maxUploadSize" value="20000000"/>
       <property name="defaultEncoding" value="utf-8"></property>
 </bean>

另外,还有一个multipartResolver需要介绍:
org.springframework.web.multipart.support.StandardServletMultipartResolver

CommonsMultipartResolver:使用commons Fileupload来处理multipart请求,使用时需导入jar包。
StandardServletMultipartResolver:是基于Servlet3.0来处理multipart请求的,所以不需要引用其他jar包,但是必须使用支持Servlet3.0的容器才可以.

原因:
如果你配置的multipartFilter的multipartResolver是CommonsMultipartResolver,即如上面springmvc.xml,
web.xml的配置,报这个错误的话,说明你配置的上传文件的解析器(CommonsMultipartResolver)根本,没有用到,而是使用这个上传文件的解析器(StandardServletMultipartResolver),而你又没有对这个解析器提供必要的配置信息,所以报错。

三、解决问题
1、解决报错
给StandardServletMultipartResolver提供配置信息即可,(注:为何这样配置,原因见https://blog.csdn.net/gao_zhennan/article/details/81327268)
web.xml

<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/applicationContext-web.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>

<!--    StandardServletMultipartResolvershu属性配置  -->
    <multipart-config>
        <max-file-size>20848820</max-file-size>
        <max-request-size>418018841</max-request-size>
        <file-size-threshold>1048576</file-size-threshold>
    </multipart-config> 
  </servlet>

至此、报错的问题已经解决。但是为什么我们配置的上传文件的解析器没有用到呢?说来话长,我们源码中见。

2,解决未使用我们自己配置的上传文件的解析器(CommonsMultipartResolver) 
MultipartFilter关键源码

public class MultipartFilter extends OncePerRequestFilter {
    public static final String DEFAULT_MULTIPART_RESOLVER_BEAN_NAME = "filterMultipartResolver";
    private String multipartResolverBeanName = DEFAULT_MULTIPART_RESOLVER_BEAN_NAME;
    private final MultipartResolver defaultMultipartResolver = new 
      StandardServletMultipartResolver();

    //将web.xml文件中为multipartFilter设置的参数(init-param)的属性值(param-value),
    //通过调用set方法设置进来,此时multipartResolverBeanName=multipartResolver

    public void setMultipartResolverBeanName(String multipartResolverBeanName) {
        this.multipartResolverBeanName = multipartResolverBeanName;
    }

    protected String getMultipartResolverBeanName() {
        return this.multipartResolverBeanName;
    }


    protected MultipartResolver lookupMultipartResolver() {//关键方法:用来查找上传文件的解析器

        WebApplicationContext wac = WebApplicationContextUtils.
        getWebApplicationContext(getServletContext());
        // 1.WebApplicationContext 是spring的上下文对象,在ContextLoaderListener加载spring的配置文件后,将生成的对应的WebApplicationContext,先放在了web.xml的上下文对象中ServletContext

        String beanName = getMultipartResolverBeanName();
        // 2.上面有这个方法,返回值为this.multipartResolverBeanName,即"multipartResolver"
        if (wac != null && wac.containsBean(beanName)) {
            // 3.1只要监听器ContextLoaderListener,加载了spirng的配置文件wac 就不会是null,
            // 现在关键的是:ContextLoaderListener加载的配置文件中是否配置了这个bean(id="multipartResolver")
            return wac.getBean(beanName, MultipartResolver.class);
        }
        else {
            //3.2如过ContextLoaderListener加载的配置文件中没有这个bean,则与之
            //对应的WebApplicationContext对象中也不包含这个bean,
            //于是wac.containsBean(beanName) 为false。
            //返回默认的解析器"StandardServletMultipartResolver"
            return this.defaultMultipartResolver;
        }
    }

有点抽象,说的再多不如去做下。在前端页面发送了一个带文件上传控件的表单,看MutilFilter是否使用我配置的文件上传的解析器(CommonsMultipartResolver)。 

wac.containsBean(beanName) beanName=”multipartResolver” 为false。说明你在spring的上下文中没有配置id=”multipartResolver”。奇怪了,我明明在springmvc.xml中配置了id=”multipartResolver”.为什么在spring对应的上下文对象中找不到呢?这里就要牵扯到新的概念了。

springmvc.xml是由DispatcherServlet加载的,然后生成了springmvc的上下文对象,称为子容器。 ContextLoaderListener加载的配置文件,生成的spring的上下文对象,称为父容器。 子容器可以使用父容器中定义的bean,反之则不行。 如上multipartResovler配置在springmvc.xnl中,即对应的bean在子容器中,而WebApplicationContext.containsBean()在父容器中是查找不到这个bean的
问题解决
1.新建一个applicationContext.xml(名字任意取),
2.将multipartResovler配置在其中。
3.重要的是要通过ContextLoaderListener来加载此文件,这样这个bean就在spring的容器里了,然后WebApplicationContext.containsBean()为true,就会使用你配置的解析器,而不是使用默认的了。
最后放张图

spring和spirngmvc父子容器介绍
(转发自https://blog.csdn.net/jml1437710575/article/details/52020936)
在百度中别人的帖子中看到一段应该是官方的原文解释,我摘抄过来并粗糙的直译一下:

Spring lets you define multiple contexts in a parent-child hierarchy.
spring允许你定义多个上下文在父子继承关系中

The applicationContext.xml defines the beans for the “root webapp context”, i.e. the context associated with the webapp.
applicationContext.xml文件是为了”根webapp应用上下文”定义bean, 也就是说它的上下文是和webapp想关联的

The spring-servlet.xml (or whatever else you call it) defines the beans for one servlet’s app context. There can be many of these in a webapp,
spring-servlet.xml文件(或是其他的你习惯的称呼)是为了一个servlet应用上下文呢定义bean. 在一个webapp中可以有多个此配置文件,

one per Spring servlet (e.g. spring1-servlet.xml for servlet spring1, spring2-servlet.xml for servlet spring2).
每一个spring的servlelt(例如: 名为spring1的servlet拥有配置文件spring1-servlet.xml, 名为spring2的servlet拥有配置文件spring2-servlet.xml).

Beans in spring-servlet.xml can reference beans in applicationContext.xml, but not vice versa.
在spring-servlet.xml中定义的bean可以直接引用在applicationContext.xml中定义的bean, 但是反过来不可以.

All Spring MVC controllers must go in the spring-servlet.xml context.
所有springmvc的Controller必须在spring-servlet.xml对应的上下文中运行.

In most simple cases, the applicationContext.xml context is unnecessary. It is generally used to contain beans that are shared between all servlets
在大多数简单的情况下, applicationContext.xml对应的上下文并不必须. 它通常用来包含那些bean用来在webapp中所有servlet之间共享.

in a webapp. If you only have one servlet, then there’s not really much point, unless you have a specific use for it.
如果你只有一个servlet, 那么实际没有什么必要定义applicationContext.xml, 除非你有特别应用.

本文转自:https://blog.csdn.net/gao_zhennan/article/details/81331218

原文地址:https://www.cnblogs.com/nizuimeiabc1/p/11031381.html