再springMVC中自定義文件上傳處理解決與原spring中MultipartResolve衝突問題

    相信很多朋友再用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();
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章