基于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] view plain copy print?
  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] view plain copy print?
  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] view plain copy print?
  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] view plain copy print?
  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!

智能推荐

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2019 ITdaan.com 粤ICP备14056181号  

赞助商广告