Java动态显示文件上传进度的简单实现

    技术2022-05-12  23

    实现文件上传的进度显示,我们先看看都有哪些问题我们要解决。 1 上传数据的处理进度跟踪 2 进度数据在用户页面的显示 就这么2个问题, 第一个问题,主要是组件的选择 必须支持数据处理侦听或通知的组件。当然,我肯定只用我自己的组件啦。基本原理是 1 使用request.getContentLength() 读取到处理数据的总长度,注意这个长度不等于文件的长度,因为Base64等编码会增加数据量,如果超过了允许的长度,直接返回-1; 2 在每读取一部分数据时(比如一行,或者64K,或者你自定义的字节数),将读取的字节数通知我们的进度跟踪程序。我取名为 UploadListener代码如下

    /**   * 处理附件上传的通知。  * 各位可以继承这个类,来实现自己的特殊处理。  *   *  @author  赵学庆 www.java2000.net  */   public   class  UploadListener  {     //  调试模式将在控制台打印出一些数据     private   boolean  debug;     //  总数据字节数     private   int  total;     //  当前已经处理的数据字节数     private   int  totalCurrent  =   0 ;     //  延迟,用来调试用,免得速度太快,根本卡看不到进度     private   int  delay  =   0 ;     /**      * 处理数据通知的方法。     * 保存已经处理的数据。并且在一定的比例进行延迟。默认每1%     * 如果不需用延迟,可以删掉内部的代码,加快速度。     *      *  @param  size 增加的字节数      */      public   void  increaseTotalCurrent( long  size)  {       this .totalCurrent  +=  size;       try   {        currentRate  =  totalCurrent  *   100   /  total;         if  (currentRate  >  lastRate)  {           if  (delay  >   0 {            Thread.sleep(delay);          }            if  (debug)  {            System.out.println( " rate= "   +  totalCurrent  +   " / "   +  total  +   " / "   +  (totalCurrent  *   100   /  total));          }           lastRate  =  currentRate;        }       }   catch  (Exception e)  {        e.printStackTrace();      }     }      /**      * 读取全部自己数     *      *  @return       */      public   int  getTotal()  {       return  total;    }      /**      * 读取已经处理的字节数     *      *  @return       */      public   int  getTotalCurrent()  {       return  totalCurrent;    }      private   long  lastRate  =   0 ;     private   long  currentRate  =   0 ;     public   int  getDelay()  {       return  delay;    }      public   void  setDelay( int  delay)  {       this .delay  =  delay;    }      public   void  setTotal( int  total)  {       this .total  =  total;    }      public   boolean  isDebug()  {       return  debug;    }      public   void  setDebug( boolean  debug)  {       this .debug  =  debug;    }   }

     3 下面我们来看上传的处理部分

      Upload upload  =   new  Upload(request);     //  增加了侦听进度的代码    UploadListener uploadListener  =   new  UploadListener();     //  这句话我们后面再讨论,这个可是关键    session.setAttribute( " uploadListener " ,uploadListener);    uploadListener.setDelay( 0 );    uploadListener.setDebug( true );    upload.setUploadListener(uploadListener);    upload.parse();     //  这句话同样重要,我们后面再讨论    session.setAttribute( " uploadListener " , null );

    4 我们再看上传的表单部分

    < script type = " text/javascript " >   function checkForm() {    $( " SHOW_FRAME " ).src = " link.jsp " ;    $( ' SUBMIT ' ).disabled = true ;    Ext.MessageBox.show( {      title:  ' Please wait... ' ,      msg:  ' Initializing... ' ,      width: 240 ,      progress: true ,      closable: false     } );    $( " MAIN_FORM " ).submit();     return   false ;  }   function setUploadProcess(total,current) {    var rate  =  Number(current) / Number(total);    Ext.MessageBox.updateProgress(rate, ' Uploading... ' + current + " / " + total);     if (Number(current) >= Number(total)) {      closeUploadProcess();    }   }   function closeUploadProcess() {    Ext.MessageBox.hide();  }   </ script >   < iframe name = " ACTION_FRAME "  id = " ACTION_FRAME "  width = " 0 "  height = " 0 " ></ iframe >   < iframe name = " SHOW_FRAME "  id = " SHOW_FRAME "  width = " 0 "  height = " 0 " ></ iframe >   < form method = " OST "  id = " MAIN_FORM "  onsubmit = " return checkForm() "  enctype = " multipart/form-data "      action = " uploadFileSave.jsp "  target = " ACTION_FRAME " >      < input type = " file "  size = " 50 "  name = " file " >       < input  type = " submit "  ID = " SUBMIT "  value = " Upload It " >   </ form >

    第一个iframe用于提交表单数据,第二个就是我们用来获取处理数据进度信息的。 提交表单很简单,target指向了我们的第一个iframe 我们看一下JS checkForm 里面第一句就是关键的读取进度信息的页面,我们在第二个iframe里面获得。然后就是弹出进度的显示框,我使用了Ext. 然后提交上传表单 setUploadProcess 用来更新进度框上面的数据,第一个参数是数据总共的大小,第二个参数是已经处理的大小。 closeUploadProcess 关闭进度框 5 最后,我们来看读取进度信息的页面

    <% @ page language = " java "  contentType = " text/html; charset=utf-8 "  pageEncoding = " utf-8 " %>   <% @include file = " ../package.inc.jsp " %>   <%     response.setHeader( " ragma " " no-cache " );    response.setHeader( " Cache-Control " " no-cache " );    response.setDateHeader( " Expires " 0 );    response.setBufferSize( 0 );    UploadListener uploadListener  =   null ;     while  (uploadListener  ==   null   ||  uploadListener.getTotalCurrent()  <=   0 {      uploadListener  =  (UploadListener) session.getAttribute( " uploadListener " );      out.print( " . " );      out.flush();      Thread.sleep( 10 );    }      long  total  =  uploadListener.getTotal();    out.println(total);     long  current;    out.flush();     while  ( true {      current  =  uploadListener.getTotalCurrent();       if  (current  >=  total)  {         break ;      }       out.println( " <script type='text/javascript'>parent.setUploadProcess(' "   +  total  +   " ',' "   +  current  +   " ');</script> " );      out.flush();      Thread.sleep( 10 );    }   %>< script type = " text/javascript " > parent.closeUploadProcess(); </ script >

    其中前面的循环,用来判断是否产生了上传的信息,如果没有则等待。 然后就是读取上传的信息,并计算后生成调用上级窗口的更新进度条的JS, 请注意out.print后面必须跟上out.flush,否则不会持续输出到客户端,也就不会看到连续的进度条变化。 总结: 上面的部分比较乱,我这里总结一下关键点。 1 在上传组件里面,把总大小和当前读取了的大小放到一个类里面,并持续更新,直到处理完毕 2 上传的进度类,放在session里面,供进度读取页面读取 3 进度读取页面,从session里面拿到数据,并返回结果。 有几个疑问解释一下。 1 由于Http协议决定了,必须等request处理完毕才会返回输出,所以不能在upload页面里进行处理进度的显示。我前面测试到1M左右的文件不成功,就是没有考虑到这个问题。所以必须单独用一个GET的程序进行读取 2 读取是一个持续不断的过程,因为上传大文件是很慢的! 3 如果你的应用服务器启用了GZIP压缩,是容器管理的,那么很不幸,因为容易必须拿到所有的数据,至少是一部分数据才会返回,所以造成我们返回的那些很少的字节经常会被截住,造成无法显示上传的连续过程。   解决方法   1) 关闭GZIP, 我想许多人不会这么做    2) 使用自定义的GZIP压缩,判断某些东西(比如URL),对他们不进行压缩处理

    测试和下载地址: http://www.java2000.net/test/testUploadFile.jsp


    最新回复(0)