servlet、struts2、springmvc中實現帶進度條的文件上傳

jopen 12年前發布 | 85K 次閱讀 文件上傳 SpringMVC

實現帶進度條的文件上傳的基本原理是這樣的:

前臺異步提交文件上傳請求,然后每隔一段時間向服務器發送請求查詢文件上傳進度。

后臺處理程序解析文件上傳請求,并且每隔一段時間將上傳進度保存在HttpSession中。(由于HttpRequest是無狀態的,因此只能保存在HttpSession中)

commons-fileupload包中的ServletFileUpload類可以注冊一個進度監聽器ProgressListener,使用它可以簡化工作(實際上也沒多大簡化,嘿嘿),不用自己處理監聽了。

前臺異步提交上傳請求的方式就不多說了。(可以使用jquery的文件上傳插件)

 

下面說說如何在servlet、struts2以及springmvc中進行處理。

 

1、servlet中帶進度條的文件上傳:

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        final HttpSession session = req.getSession();
        FileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        upload.setProgressListener(new ProgressListener() {
            @Override
            public void update(long pBytesRead, long pContentLength, int pItems) {
                int percent = (int) (((float)pBytesRead / (float)pContentLength) * 100);
                session.setAttribute("percent", percent + "%");
            }
        });
        upload.setHeaderEncoding("utf-8");
        try {
            List<FileItem> items = upload.parseRequest(req);
            for (FileItem fileItem : items) {
                if (!fileItem.isFormField()) {
                    String fileName = fileItem.getName();
                    FileOutputStream fos = new FileOutputStream(new File("c:/" + fileName));
                    InputStream is = fileItem.getInputStream();
                    byte[] buffer = new byte[256];
                    int readBytes = 0;
                    while (-1 != (readBytes = is.read(buffer))) {
                        fos.write(buffer, 0, readBytes);
                    }
                    is.close();
                    fos.close();
                }
            }
        } catch (FileUploadException e) {
            e.printStackTrace();
        }
    }

2、struts2中帶進度條的文件上傳。

struts2默認的攔截器中有一個FileUploadInterceptor,它會攔截所有的MultipartRequest,并且將得到的File及相關信息傳遞給action,因此在action被調用之前,文件上傳已經被處理完了。

struts2處理文件上傳使用的是commons-fileupload,因此我們可以使用ProgressListener。注意我們需要在解析請求的時候加入我們的監聽器,我們首先想到的是替換掉FileUploadInterceptor,不幸的是 FileUploadInterceptor并不執行解析的任務,實際在FileUploadInterceptor被調用之前,MultipartRequest已經被解析了,文件上傳的工作已經完成。

實際上對于所有的文件上傳請求,struts2會為其生成一個MultiPartRequestWrapper進行包裝,而它維護著一個 MultiPartRequest接口的實例。MultiPartRequest的實現類只有一個 JakartaMultiPartRequest,JakartaMultiPartRequest有一個方法parseRequest,此方法負責解析 request并生成FileItem。

因此我們可以重寫此方法,添加ProgressListener。不幸的是,JakartaMultiPartRequest的很多方法都是private的,我們不能繼承它然后重寫parseRequest方法。

JakartaMultiPartRequest實現了MultiPartRequest接口,我們可以編寫一個類,實現 MultiPartRequest接口,并且把JakartaMultiPartRequest的代碼全都拷貝過來,然后修改parseRequest方法。

下面是修改后的parseRequest方法:

private List<FileItem> parseRequest(HttpServletRequest servletRequest, String saveDir) throws FileUploadException {
        final HttpSession session = servletRequest.getSession();
        DiskFileItemFactory fac = createDiskFileItemFactory(saveDir);
        ServletFileUpload upload = new ServletFileUpload(fac);
        upload.setProgressListener(new ProgressListener() {
            @Override
            public void update(long pBytesRead, long pContentLength, int pItems) {
                int percent = (int) (((float)pBytesRead / (float)pContentLength) * 100);
                session.setAttribute("percent", percent + "%");
                System.out.println(percent);
            }
        });
        upload.setSizeMax(maxSize);
        return upload.parseRequest(createRequestContext(servletRequest));
    }

最后我們需要將我們編寫的類注冊到struts2的配置文件中。

strtus-default.xml  中有這么一段配置:

<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="struts" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default"/>
    <bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="jakarta" class="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope="default" />
    <constant name="struts.multipart.handler" value="jakarta" />

我們照葫蘆畫瓢即可。

在struts.xml中按下面的方式配置,覆蓋掉struts-default.xml中的配置。

<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="parser" class="parser.MultipartRequestParser" scope="default" />
    <constant name="struts.multipart.handler" value="parser" />

這樣所有的文件上傳請求都會獲得進度提示支持。

 

3、spring中帶進度條的文件上傳。

spring的DispatcherServlet在初始化的時候會去容器中查找是否有可用的MultipartResolver,如果有的話就會使用此resolver將request轉換為MultipartHttpServletRequest。

spring提供了兩個resolver,CommonsMultipartResolver,StandardServletMultipartResolver。我們可以任選其一。

CommonsMultipartResolver的parseRequest方法調用commons-fileupload的ServletFileupload完成了對request的解析工作。

我們可以覆蓋parseRequest方法,添加監聽器:

public class MyMultipartResolver extends CommonsMultipartResolver {
    @Override
    public MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {
        String encoding = "utf-8";
        FileUpload fileUpload = prepareFileUpload(encoding);
        final HttpSession session = request.getSession();
        fileUpload.setProgressListener(new ProgressListener() {
            public void update(long pBytesRead, long pContentLength, int pItems) {
                int percent = (int) (((float)pBytesRead / (float)pContentLength) * 100);

                session.setAttribute("percent", percent + "%");
            }
        });
        
        try {
            List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
            return parseFileItems(fileItems, encoding);
        }
        catch (FileUploadBase.SizeLimitExceededException ex) {
            throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);
        }
        catch (FileUploadException ex) {
            throw new MultipartException("Could not parse multipart servlet request", ex);
        }
    }
}

最后在controller的配置文件中指定resolver:

<bean id="multipartResolver" class="com.springmvc.bean.MyMultipartResolver"></bean>

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!