相信很多朋友再用springmvc时都遇见了一个问题,那就是自带的获取上传的东西太慢,而且不知道如何修改,其实不然,spring框架既然给我们开放了这个接口,就一定遵从了可扩展性的原则,经过查看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进行代理,如果是一个代理实例,则返回false,告知Spring不需要再对当前的request进行文件上传处理;如果不是,则直接调用父类的isMultipart方法
之前的项目代码不能满足我们的要求所以我们修改了,spring的CommonsMultipartResolver类来自定义我们的上传方法,大概思路时,代理当前HttpServletRequest,被代理的HttpServletRequest返回的是一个代理类,在isMultipart方法中判断有无被代理这样就可以实现我们自己的文件上传处理逻辑了
首先我们先自定义一个文件上传的过滤器Filter
web.xml
<!--文件上传过滤器--> <filter> <filter-name>MultiPartFilter</filter-name> <filter-class>com.cn.interceptor.MultiPartFilter</filter-class> <init-param> <param-name>excludeURL</param-name> <param-value>/res/js/ueditor</param-value> </init-param> </filter> <filter-mapping> <filter-name>MultiPartFilter</filter-name> <url-pattern>/res/js/ueditor/*</url-pattern> </filter-mapping>
上面配置一个文件上传过滤器,需要注意的是,这个过滤器因为代理了当前的servlet请求,所以其中的某些数据会被过滤,比如登录信息,所以一定要配置在登录过滤后面,这样才不会影响正常的框架,其他的过滤器因项目需要稍作更改
下面是代理HttpServletRequest的过滤器代码
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { boolean jump = false; HttpServletRequest request = (HttpServletRequest) servletRequest; // 根据web.xml中的配置,判断当前url是否跳过此过滤器 String excludeURL = getFilterConfig().getInitParameter("excludeURL"); if (excludeURL != null && !"".equals(excludeURL)) { String requestURI = request.getRequestURI(); if (requestURI.indexOf(excludeURL) != -1) { jump = true; } } if (jump) { String content_type = request.getContentType(); if (content_type != null && content_type.indexOf("multipart/form-data") != -1) { try { /** *解析上传的文件存入缓存中,并返回一个代理对象 */ request =parseMultipartContent(request); String s = request.getClass().toString(); System.out.println(s); } catch (Exception e) { throw new IOException(e.getMessage()); } } } filterChain.doFilter(request, servletResponse); }
接着我们继承springmvc的CommonsMultipartResolver重写其中的isMultipart
/** * spring文件文件拦截器 */ public class CommonsMultipartResolver extends org.springframework.web.multipart.commons.CommonsMultipartResolver { /** * {@inheritDoc} * 判断当前request有没有被代理 * * @see org.springframework.web.multipart.commons.CommonsMultipartResolver#isMultipart(javax.servlet.http.HttpServletRequest) */ @Override public boolean isMultipart(HttpServletRequest request) { if (request.getClass().toString().indexOf("class com.sun.proxy") != -1) { return false; } else { return super.isMultipart(request); } } }
然后再spring配置文件中修改成我们已经写好的类路径
<bean id="multipartResolver" class="com.cn.interceptor.CommonsMultipartResolver"> <property name="maxUploadSize" value="100000000" /> </bean>
至此,整个框架正常的流程代码已经完成,我们可以开心的写我们的业务代码啦
下面是一个文件上传的代码,使用java1.7中NIO,比spring代码的操作文件流快了很多
写在过滤其中的代码
/** * 解析request对象中的文件,并返回一个代理对象 * @param request * @return * @throws Exception */ private HttpServletRequest parseMultipartContent( final HttpServletRequest request) throws Exception { if (!ServletFileUpload.isMultipartContent(request)) return request; // non-file parameters final Map<String, String> requestParams = new HashMap<String, String>(); // Parse the request ServletFileUpload sfu = new ServletFileUpload(); String characterEncoding = request.getCharacterEncoding(); if (characterEncoding == null) { characterEncoding = "UTF-8"; } sfu.setHeaderEncoding(characterEncoding); FileItemIterator iter = sfu.getItemIterator(request); MultipleUploadItems uploads = new MultipleUploadItems(getTempDir(request)); while (iter.hasNext()) { FileItemStream item = iter.next(); // not a file if (item.isFormField()) { InputStream stream = item.openStream(); requestParams.put(item.getFieldName(), Streams.asString(stream, characterEncoding)); stream.close(); } else { // it is a file! String fileName = item.getName(); if (fileName != null && !"".equals(fileName.trim())) { uploads.addItemProxy(item); } } } //储存可以重用的请求,不被servlet消费 uploads.writeInto(request); requestParams.putAll(getParameterMap(request)); // 'getParameter()' method can not be called on original request object // after parsing // so we stored the request values and provide a delegate request object return (HttpServletRequest) Proxy.newProxyInstance(this.getClass() .getClassLoader(), new Class[]{HttpServletRequest.class}, new InvocationHandler() { @Override public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable { // 代理的方法 // we replace getParameter() and getParameterValues() // methods if ("getParameter".equals(arg1.getName())) { String paramName = (String) arg2[0]; return requestParams.get(paramName); } if ("getParameterValues".equals(arg1.getName())) { String paramName = (String) arg2[0]; // normalize name 'key[]' to 'key' if (paramName.endsWith("[]")) paramName = paramName.substring(0, paramName.length() - 2); if (requestParams.containsKey(paramName)) return new String[]{requestParams .get(paramName)}; // if contains key[1], key[2]... int i = 0; List<String> paramValues = new ArrayList<String>(); while (true) { String name2 = String.format("%s[%d]", paramName, i++); if (requestParams.containsKey(name2)) { paramValues.add(requestParams.get(name2)); } else { break; } } return paramValues.isEmpty() ? new String[0] : paramValues.toArray(new String[0]); } return arg1.invoke(request, arg2); } }); } /** * 返回本地的tmp路径的File对象 * @param request * @return * @throws IOException */ private File getTempDir(HttpServletRequest request) throws IOException { if (_tempDir != null) return _tempDir; _tempDir = new File(_tempDirPath); if (!_tempDir.isAbsolute()) { _tempDir = new File(request.getServletContext().getRealPath(_tempDirPath)); } _tempDir.mkdirs(); _logger.info(String.format("using temp dir: %s", _tempDir.getCanonicalPath())); return _tempDir; } /** * 获取所有的请求参数 * @return */ public Map getParameterMap(HttpServletRequest request ){ Map properties = request.getParameterMap(); Map returnMap = new HashMap(); Iterator entries = properties.entrySet().iterator(); String name = ""; String value = ""; while(entries.hasNext()) { Map.Entry entry = (Map.Entry)entries.next(); name = (String)entry.getKey(); Object valueObj = entry.getValue(); if (valueObj == null) { value = ""; } else if (!(valueObj instanceof String[])) { value = valueObj.toString(); } else { String[] values = (String[])valueObj; for(int i = 0; i < values.length; ++i) { value = values[i] + ","; } value = value.substring(0, value.length() - 1); } _logger.info("参数 " + name + " == " + value); returnMap.put(name, value); } return returnMap; }
文件缓存代理类
/** * 文件代理类 */ public class MultipleUploadItems { Logger _logger = Logger.getLogger(this.getClass()); List<FileItemStream> _items = new ArrayList<FileItemStream>(); File _tempDir; public List<FileItemStream> items() { return _items; } public MultipleUploadItems(File tempDir) { _tempDir = tempDir; } /** * find items with given form field name * * @param fieldName * @return */ public List<FileItemStream> items(String fieldName) { List<FileItemStream> filteredItems = new ArrayList<FileItemStream>(); for (FileItemStream fis : _items) { if (fis.getFieldName().equals(fieldName)) filteredItems.add(fis); } return filteredItems; } public void addItem(FileItemStream fis) { _items.add(fis); } public void addItemProxy(final FileItemStream item) throws IOException { InputStream stream = item.openStream(); //ByteArrayOutputStream os = new ByteArrayOutputStream(); //create a temp source final File source = File.createTempFile("elfinder_upload_", "", _tempDir); FileOutputStream os = new FileOutputStream(source); IOUtils.copy(stream, os); os.close(); //final byte[] bs = os.toByteArray(); stream.close(); _logger.debug(String.format("saving item: %s", source.getCanonicalPath())); addItem((FileItemStream) Proxy.newProxyInstance(this.getClass() .getClassLoader(), new Class[]{FileItemStream.class, Finalizable.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("openStream".equals(method.getName())) { //return new ByteArrayInputStream(bs); return new FileInputStream(source); } if ("finalize".equals(method.getName())) { source.delete(); _logger.debug(String.format("removing item: %s", source.getCanonicalPath())); return null; } return method.invoke(item, args); } })); } public void writeInto(HttpServletRequest request) throws FileUploadException, IOException { // store items for compatablity request.setAttribute(FileItemStream.class.getName(), _items); request.setAttribute(MultipleUploadItems.class.getName(), this); } /** * 获取临时上传的文件 * @param request * @return */ public static MultipleUploadItems loadFrom(HttpServletRequest request) { return (MultipleUploadItems) request .getAttribute(MultipleUploadItems.class.getName()); } /** * 清除临时文件 * @param request */ public static void finalize(HttpServletRequest request) { MultipleUploadItems mui = loadFrom(request); if (mui != null) { for (FileItemStream fis : mui.items()) { if (fis instanceof Finalizable) { ((Finalizable) fis).finalize(); } } } } interface Finalizable { void finalize(); } }