在原项目中,对文件上传的处理并不是使用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各司其职了。