解决自定义文件上传处理与Spring MultipartResolver的冲突问题

  在原项目中,对文件上传的处理并不是使用Spring的MultipartResolver,而是使用自定义的MultiPartFilter和HttpServletRequestWrapper结合来进行处理。

  MultiPartFilter是一个定义在web.xml中的<filter>,原理是通过判断HttpServletRequest中的contentType是否包含”multipart/form-data”,若代码中包含此字符品,则表明是一个File Upload请求,就使用JakartaMultiPartRequest来对HttpServletRequest对其进行包装。

  其代码如下:

public class MultiPartFilter extends GenericFilterBean {
 
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
 
		HttpServletRequest request = (HttpServletRequest) servletRequest;
 
		String content_type = request.getContentType();
		if (content_type != null && content_type.indexOf("multipart/form-data") != -1) {
			JakartaMultiPartRequest jakartaMultiPartRequest = new JakartaMultiPartRequest(request,
					getFilterConfig().getInitParameter("saveDir"), Integer.valueOf(
							getFilterConfig().getInitParameter("maxUploadSize")).intValue());
 
			request = jakartaMultiPartRequest;
		}
 
		filterChain.doFilter(request, servletResponse);
    }
 
}

  JakartaMultiPartRequest类继承自HttpServletRequestWrapper,在其构造方法中,我们对Request进行分析,并且保存用户上传的文件,将文件名等信息放到Request的attribute中,从而在后续的代码中可以对上传的文件进行处理。

  但在后来客户进出的对产品图片处理的新需求中,因为JakartaMultiPartRequest的问题,没有办法满足我们的要求,所以考虑到使用Spring的MultipartResolver来进行上传处理,但之前的代码不能再进行改动,否则所有处理文件上传的Controller必须更改,代价很大。但如果在Spring中进行配置MultipartResolver的配置后,发现我们旧的文件上传代码都失效了,无法上传文件。

  后来经过调试,发现在我们定义的MultiPartFilter中处理过上传的请求后,Spring的MultipartResolver又对其进行了上传处理,这样就导致我们之前设计在Request中的attribute全部丢失,所以我们得想办法让我们自定义的MultiPartFilter和Spring的MultipartResolver不要互相干扰。

我们需要经两步来解决此问题:

第一步:我们对自定义的MultiPartFilter进行改动,让其接受在web.xml中传入的配置参数“excludeURL”,此参数指定那些不需要JakartaMultiPartRequest类进行处理的页面URL。

boolean jump = false;
 
// 根据web.xml中的配置,判断当前url是否跳过此过滤器
String excludeURL = getFilterConfig().getInitParameter("excludeURL");
if (excludeURL != null && !"".equals(excludeURL)) {
	if (request.getRequestURI().indexOf(excludeURL) != -1) {
		jump = true;
	}
}
 
if (!jump) {
	String content_type = request.getContentType();
	if (content_type != null && content_type.indexOf("multipart/form-data") != -1) {
		JakartaMultiPartRequest jakartaMultiPartRequest = new JakartaMultiPartRequest(request,
				getFilterConfig().getInitParameter("saveDir"), Integer.valueOf(
						getFilterConfig().getInitParameter("maxUploadSize")).intValue());
 
		request = jakartaMultiPartRequest;
	}
}
 
filterChain.doFilter(request, servletResponse);

第二步:
  经过查看org.springframework.web.multipart.commons.CommonsMultipartResolver的源代码我们发现(我们在spring中使用commons fileupload处理上传),其中有个public boolean isMultipart(HttpServletRequest request)方法,此方法控制着Spring是否对用户的Request进行文件上传处理,于是自定义一个我们自己的CommonsMultipartResolver,继承自org.springframework.web.multipart.commons.CommonsMultipartResolver,并重写其中的public boolean isMultipart(HttpServletRequest request),在方法中对当前的request进行判断,如果是一个JakartaMultiPartRequest实例,则返回false,告知Spring不需要再对当前的request进行文件上传处理;如果不是,则直接调用父类的isMultipart方法。

代码如下:

public class CommonsMultipartResolver extends org.springframework.web.multipart.commons.CommonsMultipartResolver {
 
    /**
     * {@inheritDoc}
     * 
     * @see org.springframework.web.multipart.commons.CommonsMultipartResolver#isMultipart(javax.servlet.http.HttpServletRequest)
     */
    @Override
    public boolean isMultipart(HttpServletRequest request) {
        if (request instanceof JakartaMultiPartRequest) {
            return false;
        } else {
            return super.isMultipart(request);
        }
    }
 
}

  当然,Spring中的配置也要使用自定义的CommonsMultipartResolver,

    <bean id="multipartResolver"
        class="net.ymeng.example.web.filter.CommonsMultipartResolver">
        <property name="maxUploadSize"
            value="20000000"></property>
    </bean>

  这样,就可以让自定义的MultiPartFilter和Spring的MultipartResolver各司其职了。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注