commons-fileupload实现上传进度条的显示

  本文将使用   apache fileupload   ,spring MVC   jquery 实现一个带进度条的多文件上传, 由于fileupload 的局限,暂不能实现每个上传文件都显示进度条,只能实现一个总的进度条

优点:不用引入第三方的组件(如js框架,flash等)

缺点:如果同时上传多个文件,由于apache fileupload API的限制,只能显示一个总的进度条

1.引入环境所需要的jar包

引入上传工具类jar包

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
</dependency>
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>            

引入mvc处理json数据jar包(@requestbody&@responsebody)

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.5.1</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.5.1</version>
</dependency> 

 

2.jsp页面

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css">
<script src="//code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
<link rel="stylesheet" href="http://jqueryui.com/resources/demos/style.css">
<script type="text/javascript">  
$(document).ready(function(){  
     $('#subbut').bind('click',  
            function(){         
                $('#fForm').submit();  
                
                var eventFun = function(){  
                    $.ajax({  
                        type: 'GET',  
                        url: '${pageContext.request.contextPath}/test/process.action',  
                        data: {},  
                        dataType: 'json',  
                        success : function(data){  
                                $("#fff").html("上传进度:"+data.show);
                                $( "#proBar" ).progressbar({
                                    value: data.rate
                                  });
                                if(data.rate == 100){  
                                    window.clearInterval(intId);  
                                    alert("上传完成");
                                }     
                }});};  
                var intId = window.setInterval(eventFun,100);  
    });             
});  
</script>  
<title>Insert title here</title>
</head>
<body>
      <form id='fForm'   action="${pageContext.request.contextPath}/test/testbar.action" encType="multipart/form-data"  method="post" target="uploadf">  
                         <div>  
                            <label>上传文件:</label>  
                            <div>  
                                <input type="file"  name="file" style="width:550">  
                            </div>  
                          
                            <label id="fff"></label>  
                            <div>  
                                <div style="width:50%">  
                                    <div  id = 'proBar'></div>  
                                </div>  
                            </div>  
                        </div>  
                         <div >  
                            <div >  
                            <button type="button" id="subbut" >submit</button>  
                            </div>  
                        </div>  
    </form>  
    <iframe name="uploadf" style="display:none"></iframe>   
</body>
</html>

 

 

这里注意需要引入js和css,如果需要更换进度条的样式,可以引入其他的css(如bootstarp等)

3.后台的实现

 

import java.io.File;
import java.util.Iterator;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/test")
public class FileUploadController {
    
    private final    Logger log = Logger.getLogger(FileUploadController.class);  
    
    /** 
     * upload  上传文件 
     */  
    @RequestMapping(value = "/testbar.action", method = RequestMethod.POST)  
    public ModelAndView upload(HttpServletRequest request,HttpServletResponse response) throws Exception {  
        final HttpSession hs = request.getSession();  
        ModelAndView mv = new ModelAndView();  
        boolean isMultipart = ServletFileUpload.isMultipartContent(request);  
        if(!isMultipart){  
            return mv;  
        }  
        // Create a factory for disk-based file items  
        FileItemFactory factory = new DiskFileItemFactory();  
  
        // Create a new file upload handler  
        ServletFileUpload upload = new ServletFileUpload(factory);  
        //解决上传文件名的中文乱码
        upload.setHeaderEncoding("UTF-8"); 
        
        upload.setProgressListener(new ProgressListener(){  
               public void update(long pBytesRead, long pContentLength, int pItems) {  
                   ProcessInfo pri = new ProcessInfo();  
                   pri.itemNum = pItems;  
                   pri.readSize = pBytesRead;  
                   pri.totalSize = pContentLength;  
                   pri.show = pBytesRead+"/"+pContentLength+" byte";  
                   pri.rate = Math.round(new Float(pBytesRead) / new Float(pContentLength)*100);  
                   hs.setAttribute("proInfo", pri);  
               }  
            });  
        //     Parse the request  
        List items = upload.parseRequest(request);  
      //   Process the uploaded items  
      Iterator iter = items.iterator();  
      while (iter.hasNext()) {  
          FileItem item = (FileItem) iter.next();  
          if (item.isFormField()) {  
              String name = item.getFieldName();  
              String value = item.getString("utf-8");  
              System.out.println("this is common feild!"+name+"="+value);  
          } else {  
              System.out.println("this is file feild!");  
               String fieldName = item.getFieldName();  
                  String fileName = item.getName().substring(item.getName().lastIndexOf("\\")+1);;  
                  String contentType = item.getContentType();  
                  boolean isInMemory = item.isInMemory();  
                  long sizeInBytes = item.getSize();  
                  File uploadedFile = new File("c://"+fileName);  
                  item.write(uploadedFile);  
                 
          }  
      }  
    
        return mv;  
    }  
      
      
    /** 
     * process 获取进度 
     */  
    @RequestMapping(value = "/process.action", method = RequestMethod.GET)    
    public @ResponseBody Object process(HttpServletRequest request,HttpServletResponse response) throws Exception {  
        return ( ProcessInfo)request.getSession().getAttribute("proInfo");  
    }   
    //精度条pojo
    class ProcessInfo{  
        public long totalSize = 1;  
        public long readSize = 0;  
        public String show = "";  
        public int itemNum = 0;  
        public int rate = 0;  
    }  
}

 

 

 

4.SpringMVC中servletFileUpload.parseRequest(request)解析为空获取不到数据问题

问题产生的原因

先看一下springmvc中上传的配置

  <bean id="multipartResolver"  class="org.springframework.web.multipart.commons.CommonsMultipartResolver">  
            <property name="defaultEncoding" value="UTF-8" />  
            <property name="maxUploadSize" value="2000000000" />  
  </bean>  

 

 再看看controller中使用

方式一

 public void upload2(HttpServletRequest request) {  
            // 转型为MultipartHttpRequest  
            try {  
                MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;  
                List<MultipartFile> fileList = multipartRequest.getFiles("file");  
                for (MultipartFile mf : fileList) {  
                    if(!mf.isEmpty()){  
                          
                    }  
                }  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
              
        }  

 

方式二

 public String upload(HttpServletRequest request,  
                @RequestParam(value = "file") MultipartFile[] files) {  
            try {  
                for (MultipartFile mf : files) {  
                    if(!mf.isEmpty()){  
                          
                    }  
                }  
      
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
            return "upload";  
        }  

 

 

这里springMVC 都为我们封装好成自己的文件对象了,转换的过程就在我们所配置的CommonsMultipartResolver这个转换器里面.他的转换器里面就是调用common-fileupload的方式解析,然后再使用parseFileItems()方法封装成自己的文件对象.大家应该发现了上面的这句代码,已经使用过fileUpload解析过request了,你在Controller里面接收到的request都已经是解析过的,你再次使用upload进行解析获取到的肯定是空,这个就是问题的所在

 

解决方式一(以上案例就是用此解决方法)

1.删除mvc里面上传的配置

<!--         <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> -->
<!--          指定所上传文件的总大小不能超过200KB。注意maxUploadSize属性的限制不是针对单个文件,而是所有文件的容量之和 -->   
<!--         <property name="maxUploadSize" value="200000"/>   -->
<!--         </bean> -->

 

 2.在控制器里面自己完成request的解析(当然上面spring MVC提供的两种方法是不能用的,所有上传的地方都需要自己做处理)

public void upload3(HttpServletRequest request) {  
            DiskFileItemFactory factory = new DiskFileItemFactory();  
            ServletFileUpload upload = new ServletFileUpload(factory);  
            try {  
                List<FileItem> list = upload.parseRequest(request);  
                for(FileItem item : list){  
                    if(item.isFormField()){  
                          
                    }else{  
                        //item.write(new File(""));  
                    }  
                }  
            } catch (FileUploadException e) {  
                e.printStackTrace();  
            }  
              
        }  

 

 

解决方式二(重写listener)

 1.创建状态pojo

public class Progress {
    
     public long totalSize = 1;  
     public long readSize = 0;  
     public String show = "";  
     public int itemNum = 0;  
     public int rate = 0;
     
    
    public void setTotalSize(long totalSize) {
        this.totalSize = totalSize;
    }
    
    public void setReadSize(long readSize) {
        this.readSize = readSize;
    }
    public String getShow() {
        return readSize+"/"+totalSize+" byte";
    }
    
    public int getItemNum() {
        return itemNum;
    }
    public void setItemNum(int itemNum) {
        this.itemNum = itemNum;
    }
    public int getRate() {
        return Math.round(new Float(readSize) / new Float(totalSize)*100);
    }
}

 

2.写自己的listener

import javax.servlet.http.HttpSession;

import org.apache.commons.fileupload.ProgressListener;

public class FileUploadListener implements ProgressListener{

    
        private HttpSession session;  
      
        public void setSession(HttpSession session){  
            this.session=session;  
            Progress status = new Progress();  
            session.setAttribute("status", status);  
        }  
      
        /*  
         * pBytesRead 到目前为止读取文件的比特数 pContentLength 文件总大小 pItems 目前正在读取第几个文件  
         */  
        public void update(long pBytesRead, long pContentLength, int pItems) {  
            Progress status = (Progress) session.getAttribute("status");                         
            status.setReadSize(pBytesRead);
            status.setTotalSize(pContentLength);
            status.setItemNum(pItems);
        } 

}

 

 

3.写自己的resolver

public class CommonsMultipartResolverExt extends CommonsMultipartResolver{

     @Override  
        protected MultipartParsingResult parseRequest(HttpServletRequest request)  
                throws MultipartException {  
            FileUploadListener listener = new FileUploadListener();  
            listener.setSession(request.getSession());
            String encoding = determineEncoding(request);  
            FileUpload fileUpload = prepareFileUpload(encoding);  
            fileUpload.setProgressListener(listener);  
            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);  
            }  
        }   
}

 

 

4.配置上传resolver

    <bean id="multipartResolver" class="com.fyh.www.common.CommonsMultipartResolverExt"></bean>

 

 

5.测试

@Controller
@RequestMapping("/test2")
public class FileUploadController2 {
    
    private final    Logger log = Logger.getLogger(FileUploadController2.class);  
    
    /** 
     * upload  上传文件 
     */  
    @RequestMapping(value = "/testbar.action", method = RequestMethod.POST)  
    public ModelAndView upload(HttpServletRequest request,HttpServletResponse response) throws Exception {  
        ModelAndView mv = new ModelAndView();
        MultipartHttpServletRequest multipartRequest=(MultipartHttpServletRequest) request;
        MultipartFile file = multipartRequest.getFile("file");
        
        InputStream inputStream = file.getInputStream();
        
        FileUtils.copyInputStreamToFile(inputStream, new File("e://"+file.getOriginalFilename()));
        
        
    
        return mv;  
    }  
      
      
    /** 
     * process 获取进度 
     */  
    @RequestMapping(value = "/process.action", method = RequestMethod.GET)    
    public @ResponseBody Object process(HttpServletRequest request,HttpServletResponse response) throws Exception {  
        return ( Progress)request.getSession().getAttribute("status");  
    }   
  
}

 

 

此方案在获取监控进度的同时并不因影响mvc原有上传方法的使用

 

解决方式三(需要用到进度条的上传时,写独立的servlet,servlet+apache uploadfile  与springmvc互不干预)不建议用此方法

 

posted @ 2016-10-27 10:51  woms  阅读(6269)  评论(2编辑  收藏  举报