前段时间调查了一下可显示进度的文件上传,发现TELIO公司 的Pierre-Alexandre发表的《AJAX Upload progress monitor for Commons-FileUpload Example》 的实现不错。但是该文中用到的是FileUpload1.1版本,而FileUpload1.2版本提供了处理上传的Listener功能,该功能可大大简化对文件上传得处理,于是对原文中的程序作了一些修改。
服务器端代码:
首先定义一个保存文件上传状态的Bean,类UploadInfo将其作为服务器端与web客户端之间通信的媒介物。
public
class
UploadInfo
...
{ private long totalSize = 0 ; private long bytesRead = 0 ; private long elapsedTime = 0 ; private String status = " done " ; private int fileIndex = 0 ; public UploadInfo() ... { } public UploadInfo( int fileIndex, long totalSize, long bytesRead, long elapsedTime, String status) ... { this .fileIndex = fileIndex; this .totalSize = totalSize; this .bytesRead = bytesRead; this .elapsedTime = elapsedTime; this .status = status; } public String getStatus() ... { return status; } public void setStatus(String status) ... { this .status = status; } public long getTotalSize() ... { return totalSize; } public void setTotalSize( long totalSize) ... { this .totalSize = totalSize; } public long getBytesRead() ... { return bytesRead; } public void setBytesRead( long bytesRead) ... { this .bytesRead = bytesRead; } public long getElapsedTime() ... { return elapsedTime; } public void setElapsedTime( long elapsedTime) ... { this .elapsedTime = elapsedTime; } public boolean isInProgress() ... { return " progress " .equals(status) || " start " .equals(status); } public int getFileIndex() ... { return fileIndex; } public void setFileIndex( int fileIndex) ... { this .fileIndex = fileIndex; } }
然后定义类FileUploadListener,用来监听文件上传的状态。FileUpload1.2版本提供了ProcessListener接口,使开发者可通过FileUploadBase类对象的setProcessListener方法植入自己的Listener。类FileUploadListener实现ProcessListener接口,当文件上传状态有所改变时,将调用其update方法。
public
class
FileUploadListener
implements
ProgressListener
...
{ private HttpServletRequest request; private long delay = 0 ; private long startTime = 0 ; public FileUploadListener(HttpServletRequest request, long debugDelay) ... { this .request = request; this .delay = debugDelay; this .startTime = System.currentTimeMillis(); } public void update( long pBytesRead, long pContentLength, int pItems) ... { long delta = (System.currentTimeMillis() - startTime) / 1000 ; request.getSession().setAttribute( " uploadInfo " , new UploadInfo(pItems, pContentLength, pBytesRead,delta, "" )); } }
定义UploadMonitor类,并通过dwr的配置,使其和客户端形成映射。
public
class
UploadMonitor
...
{ public UploadInfo getUploadInfo() ... { HttpServletRequest req = WebContextFactory.get().getHttpServletRequest(); if (req.getSession().getAttribute( " uploadInfo " ) != null ) return (UploadInfo) req.getSession().getAttribute( " uploadInfo " ); else return new UploadInfo(); } }
对类UploadInfo和UploadMonitor类在dwr.xml中进行相应的配置。
<
dwr
>
<
allow
>
<
create creator
=
"
new
"
javascript
=
"
UploadMonitor
"
>
<
param name
=
"
class
"
value
=
"
be.telio.mediastore.ui.upload.UploadMonitor
"
/>
</
create
>
<
convert converter
=
"
bean
"
match
=
"
be.telio.mediastore.ui.upload.UploadInfo
"
/>
<
create creator
=
"
new
"
javascript
=
"
ReverseAjaxDemo
"
scope
=
"
application
"
>
<
param name
=
"
class
"
value
=
"
be.telio.mediastore.ui.upload.ReverseAjaxDemo
"
/>
</
create
>
</
allow
>
</
dwr
>
Web客户端代码:
为了说明问题,只提供了最基本的实现,并去掉了多余的代码。
前台采用JSP的方式,操作界面(index.jsp):
......
<
form
action
="upload.jsp"
enctype
="multipart/form-data"
method
="post"
onsubmit
="startProgress()"
>
<
input
class
="default"
type
="file"
id
="file1"
name
="file1"
/><
br
/>
<
input
type
="submit"
value
="begin upload"
id
="uploadbutton"
/>
<
div
id
="progressBar"
style
="display: none;"
>
<
div
id
="theMeter"
>
<
div
id
="progressBarText"
></
div
>
<
div
id
="progressBarBox"
>
<
div
id
="progressBarBoxContent"
></
div
>
</
div
>
</
div
>
</
div
>
</
p
>
</
form
>
......
因为这个例子重点为了说明控制文件上传的状态,所以代码中再对请求解析后没有做进一步的操作。如果想对上传得文件进一步操作,可先获得upload对象的返回值,它是一个List,其中存储的是FileItem对象,该对象可以是form表单域或是上传文件file域,FileItem类提供了一些方法可获得上传对象的相关信息,具体可参考commons fileupload组件的API文档。下面代码为upload.jsp中的主要部分:
......
<%
...
DiskFileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); upload.setProgressListener( new FileUploadListener(request, 30 )); upload.parseRequest(request);
%>
......
至此,只需在客户端循环发送请求即可。upload.js中的主要代码如下:
function
refreshProgress()
...
{ UploadMonitor.getUploadInfo(updateProgress); }
function
updateProgress(uploadInfo)
...
{ var progressPercent = Math.ceil((uploadInfo.bytesRead / uploadInfo.totalSize) * 100 ); document.getElementById( ' progressBarText ' ).innerHTML = ' upload in progress: ' + progressPercent + ' % ' ; document.getElementById( ' progressBarBoxContent ' ).style.width = parseInt(progressPercent * 3.5 ) + ' px ' ; window.setTimeout( ' refreshProgress() ' , 1000 ); return true ; }
function
startProgress()
...
{ document.getElementById( ' progressBar ' ).style.display = ' block ' ; document.getElementById( ' progressBarText ' ).innerHTML = ' upload in progress: 0% ' ; document.getElementById( ' uploadbutton ' ).disabled = true ; // wait a little while to make sure the upload has started .. window.setTimeout( " refreshProgress() " , 1500 ); return true ; }