济南网页设计|自助建站济南网站建设|聊城网站建设济南网站制作|济南网站优化 http://www.mzwkj.com www.tuanqv.com

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

实现文件上传的进度显示,我们先看看都有哪些问题我们要解决。

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

posted on 2009-10-09 15:31  路大侠  阅读(1122)  评论(0)    收藏  举报

导航

济南户外拓展|企业户外拓展 http://www.tuanqv.com