• 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

  • 相关阅读:
    mysql 中表和数据库名称不要使用 '-' 命名
    htmlElement.style 是只读属性
    chrome 远程调试相关问题
    纯小白入手 vue3.0 CLI
    纯小白入手 vue3.0 CLI
    纯小白入手 vue3.0 CLI
    《前端之路》- TypeScript (四) class 中各类属性、方法,抽象类、多态
    《前端之路》- TypeScript (三) ES5 中实现继承、类以及原理
    《前端之路》- TypeScript(二) 函数篇
    《前端之路》--- 重温 Egg.js
  • 原文地址:https://www.cnblogs.com/nizuimeiabc1/p/11031381.html
Copyright © 2020-2023  润新知