`
ckf53390
  • 浏览: 27608 次
文章分类
社区版块
存档分类
最新评论

基于AJAX的文件上传显示进度条实现

 
阅读更多

http://www.cnblogs.com/xproer/archive/2012/02/17/2355440.html

 

   基于Ajax的文件上传要实现的功能要求,要在用户提交了上传按钮请求后,客户端其页面要显示文件上传进度条。

      其整个功能时序图如图所示。

基于AJAX文件上传时序图

 

      简单的说,要实现在客户端显示进度条,需要做的是:当客户端提交上传文件请求后,服务器在上传文件的过程中,将上传进度情况保存到Session中,客户端周期性的发送请求来获取保存在Session中值,以获取上传文件的进度信息。

1. 新建web工程AjaxUpload。

2. 将commons-fileupload-1.2.1-bin.zip包中的commons-fileupload-1.2.1.jar文件和commons-io-1.4-bin.zip包中的commons-io-1.4.jar文件拷贝到web工程下的WEB-INF\lib目录下。

3. 由于本实例涉及到多个类,处理此类问题最好是给相应的类打包进行管理。在web工程src目录下新建一个包com.ncu.upload。

4. 服务器端实现。

首先要创建一个用来保存文件上传状态的类 FileUploadStatus。其源码如下:

Java代码  收藏代码
  1. package com.ncu.upload;  
  2.   
  3. import java.util.*;  
  4.   
  5. public class FileUploadStatus {  
  6.     //上传总量  
  7.     private long uploadTotalSize=0;  
  8.     //读取上传总量  
  9.     private long readTotalSize=0;  
  10.     //当前上传文件号  
  11.     private int currentUploadFileNum=0;  
  12.     //成功读取上传文件数  
  13.     private int successUploadFileCount=0;  
  14.     //状态  
  15.     private String status="";  
  16.     //处理起始时间  
  17.     private long processStartTime=0l;  
  18.     //处理终止时间  
  19.     private long processEndTime=0l;  
  20.     //处理执行时间  
  21.     private long processRunningTime=0l;  
  22.     //上传文件URL列表  
  23.     private List uploadFileUrlList=new ArrayList();  
  24.     //取消上传  
  25.     private boolean cancel=false;  
  26.     //上传base目录  
  27.     private String baseDir="";  
  28.       
  29.     public String getBaseDir() {  
  30.         return baseDir;  
  31.     }  
  32.     public void setBaseDir(String baseDir) {  
  33.         this.baseDir = baseDir;  
  34.     }  
  35.     public boolean getCancel() {  
  36.         return cancel;  
  37.     }  
  38.     public void setCancel(boolean cancel) {  
  39.         this.cancel = cancel;  
  40.     }  
  41.     public List getUploadFileUrlList() {  
  42.         return uploadFileUrlList;  
  43.     }  
  44.     public void setUploadFileUrlList(List uploadFileUrlList) {  
  45.         this.uploadFileUrlList = uploadFileUrlList;  
  46.     }  
  47.     public long getProcessRunningTime() {  
  48.         return processRunningTime;  
  49.     }  
  50.     public void setProcessRunningTime(long processRunningTime) {  
  51.         this.processRunningTime = processRunningTime;  
  52.     }  
  53.     public long getProcessEndTime() {  
  54.         return processEndTime;  
  55.     }  
  56.     public void setProcessEndTime(long processEndTime) {  
  57.         this.processEndTime = processEndTime;  
  58.     }  
  59.     public long getProcessStartTime() {  
  60.         return processStartTime;  
  61.     }  
  62.     public void setProcessStartTime(long processStartTime) {  
  63.         this.processStartTime = processStartTime;  
  64.     }  
  65.     public long getReadTotalSize() {  
  66.         return readTotalSize;  
  67.     }  
  68.     public void setReadTotalSize(long readTotalSize) {  
  69.         this.readTotalSize = readTotalSize;  
  70.     }  
  71.     public int getSuccessUploadFileCount() {  
  72.         return successUploadFileCount;  
  73.     }  
  74.     public void setSuccessUploadFileCount(int successUploadFileCount) {  
  75.         this.successUploadFileCount = successUploadFileCount;  
  76.     }  
  77.     public int getCurrentUploadFileNum() {  
  78.         return currentUploadFileNum;  
  79.     }  
  80.     public void setCurrentUploadFileNum(int currentUploadFileNum) {  
  81.         this.currentUploadFileNum = currentUploadFileNum;  
  82.     }  
  83.     public String getStatus() {  
  84.         return status;  
  85.     }  
  86.     public void setStatus(String status) {  
  87.         this.status = status;  
  88.     }  
  89.     public long getUploadTotalSize() {  
  90.         return uploadTotalSize;  
  91.     }  
  92.     public void setUploadTotalSize(long uploadTotalSize) {  
  93.         this.uploadTotalSize = uploadTotalSize;  
  94.     }  
  95.       
  96. }  

 

      由于要在客户端要显示进度条,所以在上传过程中服务器端需要监视和维护上传状态的信息,此过程需要处理的数据信息是:不断更新Session中保存的FileUploadStatus实例的信息,如:已经上传的字节数,上传文件的总大小等。FileUpload现在的1.2版本为监视上传进度提供了内建的支持,可以直接继承类ProgressListener,然后重载update()方法,在该方法中添加自己要处理的代码,最后在文件上传处理代码(后面会讲到)中通过为ServletFileUpload对象注册创建的监听类。监听类UploadListener的源代码如下:

Java代码  收藏代码
  1. package com.ncu.upload;  
  2.   
  3. import javax.servlet.http.HttpSession;  
  4.   
  5. import org.apache.commons.fileupload.ProgressListener;  
  6.   
  7. public class UploadListener implements ProgressListener {  
  8.       
  9.     private HttpSession session=null;  
  10.       
  11.     public UploadListener (HttpSession session){  
  12.         this.session=session;  
  13.     }  
  14.     /** 
  15.      * 更新状态 
  16.      * @param pBytesRead 读取字节总数 
  17.      * @param pContentLength 数据总长度 
  18.      * @param pItems 当前正在被读取的field号 
  19.      */  
  20.     public void update(long pBytesRead, long pContentLength, int pItems) {  
  21.         FileUploadStatus fuploadStatus = UploadServlet.takeOutFileUploadStatusBean(this.session);  
  22.         fuploadStatus.setUploadTotalSize(pContentLength);  
  23.         //读取完成  
  24.         if (pContentLength == -1) {  
  25.             fuploadStatus.setStatus("完成对" + pItems + "个文件的读取:读取了 " + pBytesRead + "/"  + pContentLength+ " bytes.");  
  26.             fuploadStatus.setReadTotalSize(pBytesRead);  
  27.             fuploadStatus.setCurrentUploadFileNum(pItems);  
  28.             fuploadStatus.setProcessEndTime(System.currentTimeMillis());  
  29.             fuploadStatus.setProcessRunningTime(fuploadStatus.getProcessEndTime());  
  30.         }else{//读取过程中  
  31.                fuploadStatus.setStatus("当前正在处理第" + pItems+"个文件:已经读取了 " + pBytesRead + " / " + pContentLength+ " bytes.");  
  32.                fuploadStatus.setReadTotalSize(pBytesRead);  
  33.                fuploadStatus.setCurrentUploadFileNum(pItems);  
  34.                fuploadStatus.setProcessRunningTime(System.currentTimeMillis());  
  35.         }  
  36.         //System.out.println("已经读取:" + pBytesRead);  
  37.         UploadServlet.storeFileUploadStatusBean(this.session, fuploadStatus);  
  38.     }  
  39.   
  40. }  

 

     有了前面两个类的基础,下来我们可以动手去实现真正处理整个操作Servlet类。源代码如下。

Java代码  收藏代码
  1. package com.ncu.upload;  
  2.   
  3. import java.io.*;  
  4. import java.util.List;  
  5.   
  6. import javax.servlet.ServletException;  
  7. import javax.servlet.http.HttpServletRequest;  
  8. import javax.servlet.http.HttpServletResponse;  
  9. import javax.servlet.http.HttpSession;  
  10.   
  11. import org.apache.commons.fileupload.FileItem;  
  12. import org.apache.commons.fileupload.FileUploadException;  
  13. import org.apache.commons.fileupload.disk.DiskFileItemFactory;  
  14. import org.apache.commons.fileupload.servlet.*;  
  15.   
  16. /** 
  17.  * Servlet implementation class for Servlet: UploadServlet 
  18.  * 
  19.  */  
  20.  public class UploadServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {  
  21.    static final long serialVersionUID = 1L;  
  22.      
  23.      public static final String UPLOAD_STATUS="UPLOAD_STATUS";  
  24.      public static final String UPLOAD_DIR="/upload";  
  25.      
  26.     public UploadServlet() {  
  27.         super();  
  28.     }    
  29.       
  30.     /** 
  31.      * 从文件路径中取出文件名 
  32.      * @param filePath 
  33.      * @return 
  34.      */  
  35.     private String takeOutFileName(String filePath){  
  36.         int pos=filePath.lastIndexOf(File.separator);  
  37.         if (pos>0){  
  38.             return filePath.substring(pos+1);  
  39.         }  
  40.         else{  
  41.             return filePath;  
  42.         }  
  43.     }  
  44.       
  45.     /** 
  46.      * 从request中取出FileUploadStatus Bean 
  47.      * @param request 
  48.      * @return 
  49.      */  
  50.     public static FileUploadStatus takeOutFileUploadStatusBean(HttpSession session){  
  51.         Object obj=session.getAttribute(UPLOAD_STATUS);  
  52.         if (obj!=null){  
  53.             return (FileUploadStatus)obj;  
  54.         }  
  55.         else{  
  56.             return null;  
  57.         }  
  58.     }  
  59.       
  60.     /** 
  61.      * 把FileUploadStatus Bean保存到session 
  62.      * @param request 
  63.      * @param uploadStatusBean 
  64.      */  
  65.     public static void storeFileUploadStatusBean(  
  66.             HttpSession session,  
  67.             FileUploadStatus uploadStatusBean){  
  68.         session.setAttribute(UPLOAD_STATUS,uploadStatusBean);  
  69.     }  
  70.       
  71.     /** 
  72.      * 删除已经上传的文件 
  73.      * @param request 
  74.      */  
  75.     private void deleteUploadedFile(HttpServletRequest request){  
  76.         FileUploadStatus fUploadStatus=takeOutFileUploadStatusBean(request.getSession());  
  77.         for(int i=0;i<fUploadStatus.getUploadFileUrlList().size();i++){  
  78.             File uploadedFile = new File(request.getRealPath(UPLOAD_DIR)+  
  79.                     File.separator+fUploadStatus.getUploadFileUrlList().get(i));  
  80.             uploadedFile.delete();  
  81.         }  
  82.         fUploadStatus.getUploadFileUrlList().clear();  
  83.         fUploadStatus.setStatus("删除已上传的文件");  
  84.         storeFileUploadStatusBean(request.getSession(),fUploadStatus);  
  85.     }  
  86.       
  87.     /** 
  88.      * 上传过程中出错处理 
  89.      * @param request 
  90.      * @param errMsg 
  91.      * @throws IOException  
  92.      * @throws ServletException  
  93.      */  
  94.     private void uploadExceptionHandle(  
  95.             HttpServletRequest request,  
  96.             String errMsg) throws ServletException, IOException{  
  97.         //首先删除已经上传的文件  
  98.         deleteUploadedFile(request);  
  99.         FileUploadStatus fUploadStatus=takeOutFileUploadStatusBean(request.getSession());  
  100.         fUploadStatus.setStatus(errMsg);  
  101.         storeFileUploadStatusBean(request.getSession(),fUploadStatus);  
  102.     }  
  103.       
  104.     /** 
  105.      * 初始化文件上传状态Bean 
  106.      * @param request 
  107.      * @return 
  108.      */  
  109.     private FileUploadStatus initFileUploadStatusBean(HttpServletRequest request){  
  110.         FileUploadStatus fUploadStatus=new FileUploadStatus();  
  111.         fUploadStatus.setStatus("正在准备处理");  
  112.         fUploadStatus.setUploadTotalSize(request.getContentLength());  
  113.         fUploadStatus.setProcessStartTime(System.currentTimeMillis());  
  114.         fUploadStatus.setBaseDir(request.getContextPath()+UPLOAD_DIR);  
  115.         return fUploadStatus;  
  116.     }  
  117.       
  118.     /** 
  119.      * 处理文件上传 
  120.      * @param request 
  121.      * @param response 
  122.      * @throws IOException  
  123.      * @throws ServletException  
  124.      */  
  125.     private void processFileUpload(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{  
  126.         DiskFileItemFactory factory = new DiskFileItemFactory();  
  127.         //设置内存阀值,超过后写入临时文件  
  128.         //factory.setSizeThreshold(10240000*5);  
  129.         //设置临时文件存储位置  
  130.         //factory.setRepository(new File(request.getRealPath("/upload/temp")));  
  131.         ServletFileUpload upload = new ServletFileUpload(factory);  
  132.         //设置单个文件的最大上传size  
  133.         //upload.setFileSizeMax(10240000*5);  
  134.         //设置整个request的最大size  
  135.         //upload.setSizeMax(10240000*5);  
  136.         //注册监听类  
  137.         upload.setProgressListener(new UploadListener(request.getSession()));  
  138.         //保存初始化后的FileUploadStatus Bean  
  139.         storeFileUploadStatusBean(request.getSession(),initFileUploadStatusBean(request));  
  140.   
  141.         try {  
  142.             List items = upload.parseRequest(request);  
  143.             //处理文件上传  
  144.             for(int i=0;i<items.size();i++){  
  145.                 FileItem item=(FileItem)items.get(i);  
  146.   
  147.                 //取消上传  
  148.                 if (takeOutFileUploadStatusBean(request.getSession()).getCancel()){  
  149.                     deleteUploadedFile(request);  
  150.                     break;  
  151.                 }  
  152.                 //保存文件  
  153.                 else if (!item.isFormField() && item.getName().length()>0){  
  154.                     String fileName=takeOutFileName(item.getName());  
  155.                     File uploadedFile = new File(request.getRealPath(UPLOAD_DIR)+File.separator+fileName);  
  156.                     item.write(uploadedFile);  
  157.                     //更新上传文件列表  
  158.                     FileUploadStatus fUploadStatus=takeOutFileUploadStatusBean(request.getSession());  
  159.                     fUploadStatus.getUploadFileUrlList().add(fileName);  
  160.                     storeFileUploadStatusBean(request.getSession(),fUploadStatus);  
  161.                     Thread.sleep(500);  
  162.                 }  
  163.             }  
  164.           
  165.         } catch (FileUploadException e) {  
  166.             e.printStackTrace();  
  167.             //uploadExceptionHandle(request,"上传文件时发生错误:"+e.getMessage());  
  168.         } catch (Exception e) {  
  169.             // TODO Auto-generated catch block  
  170.             e.printStackTrace();  
  171.             //uploadExceptionHandle(request,"保存上传文件时发生错误:"+e.getMessage());  
  172.         }  
  173.     }  
  174.       
  175.     /** 
  176.      * 回应上传状态查询 
  177.      * @param request 
  178.      * @param response 
  179.      * @throws IOException 
  180.      */  
  181.     private void responseFileUploadStatusPoll(HttpServletRequest request,HttpServletResponse response) throws IOException{  
  182.         FileUploadStatus fUploadStatus=(FileUploadStatus)request.getSession().getAttribute(UPLOAD_STATUS);  
  183.         //计算上传完成的百分比  
  184.         long percentComplete = (long)Math.floor(((double) fUploadStatus.getReadTotalSize()/(double) fUploadStatus.getUploadTotalSize())*100.0);  
  185.         System.out.println("com:"+percentComplete);  
  186.         response.setContentType("text/xml");  
  187.         response.setCharacterEncoding("UTF-8");  
  188.         response.setHeader("Cache-Control""no-cache");  
  189.         if ( ((long)fUploadStatus.getReadTotalSize() == (long)fUploadStatus.getUploadTotalSize()) || (fUploadStatus.getCancel() == true)){  
  190.         response.getWriter().write(fUploadStatus.getStatus().toString()+"success");  
  191.         }else{  
  192.             response.getWriter().write(fUploadStatus.getStatus().toString()+"<div class=\"prog-border\"><div class=\"prog-bar\" style=\"width: "  
  193.                                 + percentComplete + "%;\"></div></div>");  
  194.         }  
  195.     }  
  196.     /** 
  197.      * 处理取消文件上传 
  198.      * @param request 
  199.      * @param response 
  200.      * @throws IOException 
  201.      */  
  202.     private void processCancelFileUpload(HttpServletRequest request,HttpServletResponse response) throws IOException{  
  203.         FileUploadStatus fUploadStatus=(FileUploadStatus)request.getSession().getAttribute(UPLOAD_STATUS);  
  204.         fUploadStatus.setCancel(true);  
  205.         request.getSession().setAttribute(UPLOAD_STATUS, fUploadStatus);  
  206.         responseFileUploadStatusPoll(request,response);  
  207.   
  208.     }  
  209.       
  210.     /** 
  211.      * 在上传文件列表中查找与文件名相关的id 
  212.      * @param request 
  213.      * @param fileName 文件名 
  214.      * @return 找到返回id,否则返回-1 
  215.      */  
  216.     private int findFileIdInFileUploadedList(HttpServletRequest request,String fileName){  
  217.         FileUploadStatus fileUploadStatus=takeOutFileUploadStatusBean(request.getSession());  
  218.         for(int i=0;i<fileUploadStatus.getUploadFileUrlList().size();i++){  
  219.             if (fileName.equals((String)fileUploadStatus.getUploadFileUrlList().get(i))){  
  220.                 return i;  
  221.             }  
  222.         }  
  223.         return -1;  
  224.     }  
  225.       
  226.     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  
  227.         doPost(request,response);  
  228.     }     
  229.       
  230.   
  231.     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  
  232.         boolean isMultipart = ServletFileUpload.isMultipartContent(request);  
  233.           
  234.         if (isMultipart) {  
  235.             processFileUpload(request,response);  
  236.         }else{  
  237.             request.setCharacterEncoding("UTF-8");  
  238.               
  239.             if (request.getParameter("uploadStatus")!=null){  
  240.                 responseFileUploadStatusPoll(request,response);  
  241.             }  
  242.             if (request.getParameter("cancelUpload")!=null){  
  243.                 processCancelFileUpload(request,response);  
  244.             }  
  245.         }  
  246.           
  247.     }                 
  248. }  

 

至此,服务器端的代码已经基本完成。

 

 

5. 客户端实现

 

由于在上传文件时需要在同一页面显示对应的进度条控件,因此,在提交表单时当前页面不能被刷新。我们可以通过将表单提交至一个隐藏的 iframe 中来实现。关于Ajax的技术前面讲过,这里就不再细说,直接给出源代码如下:

Html代码  收藏代码
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  
  2. <html>  
  3. <head>  
  4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
  5. <title>基于Ajax的上传文件显示进度条</title>  
  6.  <style>  
  7.   .prog-border {  
  8.   height: 15px;  
  9.   width: 205px;  
  10.   background: #fff;  
  11.   border: 1px solid #000;  
  12.   margin: 0;  
  13.   padding: 0;  
  14.   }  
  15.   .prog-bar {  
  16.   height: 11px;  
  17.   margin: 2px;  
  18.   padding: 0px;  
  19.   background: #178399;  
  20.   font-size: 10pt;  
  21.   }  
  22.   body{  
  23.     font-family: Arial, Helvetica, sans-serif;  
  24.     font-size: 10pt;  
  25.   }  
  26.   </style>  
  27. <script language="javascript" type="text/javascript">  
  28. <!--  
  29.     //var userName=document.getElementById("userName").value;  
  30.     //创建跨浏览器的XMLHttpRequest对象  
  31.     var timer;  
  32. function startListener(){  
  33.     var xmlhttp;  
  34.     try{  
  35.     //IE 5.0   
  36.         xmlhttp = new ActiveXObject('Msxm12.XMLHTTP');  
  37.     }catch(e){  
  38.         try{  
  39.         //IE 5.5 及更高版本  
  40.             xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');  
  41.         }catch(e){  
  42.             try{  
  43.             //其他浏览器  
  44.                 xmlhttp = new XMLHttpRequest();  
  45.             }catch(e){}  
  46.         }  
  47.     }  
  48.     var progressStatusText = document.getElementById("progressBar");  
  49.     xmlhttp.open("get","UploadServlet?uploadStatus=true",true);  
  50.     /**此处Header设置非常重要,必须设置Content-type类型,负责会报错误  
  51.     */  
  52.      xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");  
  53.      xmlhttp.onreadystatechange = function(){  
  54.         if(xmlhttp.readyState == 4){  
  55.             if(xmlhttp.status == 200){  
  56.             progressStatusText.innerHTML = "";  
  57.             progressStatusText.innerHTML = xmlhttp.responseText;  
  58.             var temp = xmlhttp.responseText.indexOf("success");  
  59.             if (  temp > 0 ){  
  60.             window.clearTimeout(timer);  
  61.             }else{  
  62.             timer = window.setTimeout(startListener,1000);  
  63.             }  
  64.             }  
  65.         }  
  66.     }  
  67.     xmlhttp.send(null);  
  68. }  
  69. function startUpload(){  
  70.     timer = window.setTimeout(startListener,1000);  
  71.     return true;  
  72. }  
  73. function cancelUpload(){  
  74.     var xmlhttp;  
  75.     try{  
  76.     //IE 5.0   
  77.         xmlhttp = new ActiveXObject('Msxm12.XMLHTTP');  
  78.     }catch(e){  
  79.         try{  
  80.         //IE 5.5 及更高版本  
  81.             xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');  
  82.         }catch(e){  
  83.             try{  
  84.             //其他浏览器  
  85.                 xmlhttp = new XMLHttpRequest();  
  86.             }catch(e){}  
  87.         }  
  88.     }  
  89.     var progressStatusText = document.getElementById("progressBar");  
  90.     xmlhttp.open("get","UploadServlet?cancelUpload=true",true);  
  91.      xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");  
  92.     //xmlhttp.setRequestHeader("Content-type", "multipart/form-data");  
  93.     xmlhttp.onreadystatechange = function(){  
  94.         if(xmlhttp.readyState == 4){  
  95.             if(xmlhttp.status == 200){  
  96.             progressStatusText.innerHTML = "";  
  97.             progressStatusText.innerHTML = xmlhttp.responseText;  
  98.             }  
  99.         }  
  100.     }  
  101.     xmlhttp.send(null);  
  102.     return false;  
  103. }  
  104. //-->  
  105. </script>  
  106. </head>  
  107. <body>  
  108. <div id="controlPanel">  
  109.     <!-- 这个是隐藏的<iframe>作为表单提交后处理的后台目标  
  110.         通过表单form的target属性指定该<iframe>将返回信息显示在<iframe>框架中  
  111.   -->  
  112.   <iframe id='target_upload' name='target_upload' src='' style='display: none'></iframe>  
  113.     <form id="fileUploadForm" name="fileUploadForm" action="UploadServlet"   
  114.         enctype="multipart/form-data" method="post" onsubmit="return startUpload();" target="target_upload">  
  115.     <input type="file" name="file" id="file" size="40"/><br>  
  116.     <input type="submit" name="uploadButton" id="uploadButton" value="开始上传"/>  
  117.     <input type="button" name="cancelUploadButton" id="cancelUploadButton" value="取消上传" onclick="return cancelUpload();"/><br>  
  118.     </form>     
  119.     <div id="progressBar">  
  120.    </div>    
  121. </div>  
  122. </body>  
  123. </html>  

 

     至此,整个文件上传的实现到此完成,读者可以在此基础上,发挥自己的创新能力,去完善此实例。

Good Luck!

基于AJAX的文件上传显示进度条实现

分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

Global site tag (gtag.js) - Google Analytics