Web形式的文件上传向来是友好的, 进度难以确定。 以前一些办法是使用meta fresh定时刷新页面来查询
上传进度, 刷屏刷IFRAME之类的页面闪烁.
用Ajax来定时查询进度, 页面的效果就好多了. 但是苦的就是一堆的JS了。
Java为例, Apache commons file upload组件, 可能是在1.0的版本之后添加了一个支持处理进度的回调
接口,org.apache.commons.fileupload.ProgressListener
/** Updates the listeners status information.
* @param pBytesRead The total number of bytes, which have been read
* so far.
* @param pContentLength The total number of bytes, which are being
* read. May be -1, if this number is unknown.
* @param pItems The number of the field, which is currently being
* read. (0 = no item so far, 1 = first item is being read, ...)
*/
void update(long pBytesRead, long pContentLength, int pItems);
如果FileUpload设置了ProgressListener的话,上传每读取若干个字节都会调用这个方法。
可以把这些文件总长,读取总字节数有用信息放在session里面,等前台Ajax call来查询这个状态.
以Spring MVC为例:
<!--CommonsMultipartResolver to upload file -->
<bean id="multipartResolver" class="com.xxxxx.web.MonitoredCommonsMultipartResolver">
<!-- <property name="maxUploadSize"><value>52428800</value></property> -->
</bean>
public class MonitoredCommonsMultipartResolver extends CommonsMultipartResolver {
/**
* Add ProgressListener
* resolveMultipart
* @see org.springframework.web.multipart.commons.CommonsMultipartResolver#resolveMultipart(javax.servlet.http.HttpServletRequest)
*/
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
String encoding = determineEncoding(request);
FileUpload fileUpload = prepareFileUpload(encoding);
ProgressListener pListener = new DefaultProgressListener(request);
fileUpload.setProgressListener(pListener);
try {
List fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
MultipartParsingResult parsingResult = parseFileItems(fileItems, encoding);
return new DefaultMultipartHttpServletRequest(
request, parsingResult.getMultipartFiles(), parsingResult.getMultipartParameters());
}
catch (FileUploadBase.SizeLimitExceededException ex) {
throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);
}
catch (FileUploadException ex) {
throw new MultipartException("Could not parse multipart servlet request", ex);
}
}
}
public class DefaultProgressListener implements ProgressListener {
private static final Log log = LogFactory.getLog(DefaultProgressListener.class);
private HttpSession session = null;
public DefaultProgressListener(){
}
public DefaultProgressListener(HttpServletRequest request){
this.session = request.getSession(false);
MultipartFileStatus multipartFileStatus = new MultipartFileStatus();
session.setAttribute(Constants.MULTIPART_FILE_STATUS, multipartFileStatus);
}
/**
* update
* @see org.apache.commons.fileupload.ProgressListener#update(long, long, int)
*/
public void update(long bytesRead, long contentLength, int items) {
if (log.isDebugEnabled())
log.debug("bytesRead=" + bytesRead + ",contentLength=" + contentLength + ",items=" + items);
MultipartFileStatus multipartFileStatus = (MultipartFileStatus)
session.getAttribute(Constants.MULTIPART_FILE_STATUS);
multipartFileStatus.setStatus(bytesRead, contentLength);
}
}
public class MultipartFileStatus {
private long contentLength = 0;
private long bytesRead = 0;
public MultipartFileStatus() {
}
/**
* @return the contentLength
*/
public long getContentLength() {
return contentLength;
}
/**
* @param contentLength the contentLength to set
*/
public void setContentLength(long contentLength) {
this.contentLength = contentLength;
}
/**
* @return the bytesRead
*/
public long getBytesRead() {
return bytesRead;
}
/**
* @param bytesRead the bytesRead to set
*/
public void setBytesRead(long bytesRead) {
this.bytesRead = bytesRead;
}
/**
*
* @param pBytesRead
* @param pContentLength
*/
public void setStatus(long pBytesRead, long pContentLength) {
this.bytesRead = pBytesRead;
this.contentLength = pContentLength;
}
/**
* Return Json style string
* @return
*/
public String getJsonStyleStatus() {
StringBuffer buf = new StringBuffer();
buf.append("{bytesRead:").append(this.bytesRead).
append(",contentLength:").append(this.contentLength).append('}');
return buf.toString();
}
}
<script type="text/javascript">
//Set the mainForm property
var mainForm=Ext.getDom('mainForm');
mainForm.enctype='multipart/form-data';
//==== Progress bar ====
var uploadProgressBar = new Ext.ProgressBar({
text:'Uploading ...'
});
Ext.onReady(function(){
});
//Start upload
function startUpload() {
//FIXME add validation later.
Ext.Ajax.timeout = 1000*3600;
Ext.Ajax.request({
method:'post',
form: 'mainForm',
params:'method=upload',
url: 'uploadImageHandler.html',
success: uploadSuccess,
failure: uploadFail
});
if (!uploadProgressBar.rendered) uploadProgressBar.render('uploadProgressBarContainer');
uploadProgressBar.updateProgress(0,'Start uploading...');
//Start Timer thread
Ext.TaskMgr.start({
run:function(){
Ext.Ajax.request({
method:'post',
params:'method=queryStatus',
url: 'uploadImageHandler.html',
success: queryUploadSuccess,
failure: queryUploadFail
});
},
interval: 1000
});
}
function uploadSuccess(response){
alert(response.responseText + " upload is successful!");
}
function uploadFail(response) {
alert("Upload image is failed.");
Ext.TaskMgr.stopAll();
}
function queryUploadSuccess(response) {
//alert(response.responseText + " query success!");
var fileStatus = Ext.decode(response.responseText);
var percentage=fileStatus.bytesRead/fileStatus.contentLength;
uploadProgressBar.updateProgress(percentage, (percentage * 100) + "%");
if (percentage == 1)
Ext.TaskMgr.stopAll();
}
function queryUploadFail(response) {
alert("Ajax call query status fails!");
Ext.TaskMgr.stopAll();
}
</script>
很粗的一个测试例子, 好好完善Cancel之类的按钮也是可以加上去的。网上也有很多相似的文章, 本文主要是
在Spring MVC那个专有些, 使用Ext 1.1.1 + 2.0的progress bar widget, 做页面是个苦事.
上传进度, 刷屏刷IFRAME之类的页面闪烁.
用Ajax来定时查询进度, 页面的效果就好多了. 但是苦的就是一堆的JS了。
Java为例, Apache commons file upload组件, 可能是在1.0的版本之后添加了一个支持处理进度的回调
接口,org.apache.commons.fileupload.ProgressListener
/** Updates the listeners status information.
* @param pBytesRead The total number of bytes, which have been read
* so far.
* @param pContentLength The total number of bytes, which are being
* read. May be -1, if this number is unknown.
* @param pItems The number of the field, which is currently being
* read. (0 = no item so far, 1 = first item is being read, ...)
*/
void update(long pBytesRead, long pContentLength, int pItems);
如果FileUpload设置了ProgressListener的话,上传每读取若干个字节都会调用这个方法。
可以把这些文件总长,读取总字节数有用信息放在session里面,等前台Ajax call来查询这个状态.
以Spring MVC为例:
<!--CommonsMultipartResolver to upload file -->
<bean id="multipartResolver" class="com.xxxxx.web.MonitoredCommonsMultipartResolver">
<!-- <property name="maxUploadSize"><value>52428800</value></property> -->
</bean>
public class MonitoredCommonsMultipartResolver extends CommonsMultipartResolver {
/**
* Add ProgressListener
* resolveMultipart
* @see org.springframework.web.multipart.commons.CommonsMultipartResolver#resolveMultipart(javax.servlet.http.HttpServletRequest)
*/
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
String encoding = determineEncoding(request);
FileUpload fileUpload = prepareFileUpload(encoding);
ProgressListener pListener = new DefaultProgressListener(request);
fileUpload.setProgressListener(pListener);
try {
List fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
MultipartParsingResult parsingResult = parseFileItems(fileItems, encoding);
return new DefaultMultipartHttpServletRequest(
request, parsingResult.getMultipartFiles(), parsingResult.getMultipartParameters());
}
catch (FileUploadBase.SizeLimitExceededException ex) {
throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);
}
catch (FileUploadException ex) {
throw new MultipartException("Could not parse multipart servlet request", ex);
}
}
}
public class DefaultProgressListener implements ProgressListener {
private static final Log log = LogFactory.getLog(DefaultProgressListener.class);
private HttpSession session = null;
public DefaultProgressListener(){
}
public DefaultProgressListener(HttpServletRequest request){
this.session = request.getSession(false);
MultipartFileStatus multipartFileStatus = new MultipartFileStatus();
session.setAttribute(Constants.MULTIPART_FILE_STATUS, multipartFileStatus);
}
/**
* update
* @see org.apache.commons.fileupload.ProgressListener#update(long, long, int)
*/
public void update(long bytesRead, long contentLength, int items) {
if (log.isDebugEnabled())
log.debug("bytesRead=" + bytesRead + ",contentLength=" + contentLength + ",items=" + items);
MultipartFileStatus multipartFileStatus = (MultipartFileStatus)
session.getAttribute(Constants.MULTIPART_FILE_STATUS);
multipartFileStatus.setStatus(bytesRead, contentLength);
}
}
public class MultipartFileStatus {
private long contentLength = 0;
private long bytesRead = 0;
public MultipartFileStatus() {
}
/**
* @return the contentLength
*/
public long getContentLength() {
return contentLength;
}
/**
* @param contentLength the contentLength to set
*/
public void setContentLength(long contentLength) {
this.contentLength = contentLength;
}
/**
* @return the bytesRead
*/
public long getBytesRead() {
return bytesRead;
}
/**
* @param bytesRead the bytesRead to set
*/
public void setBytesRead(long bytesRead) {
this.bytesRead = bytesRead;
}
/**
*
* @param pBytesRead
* @param pContentLength
*/
public void setStatus(long pBytesRead, long pContentLength) {
this.bytesRead = pBytesRead;
this.contentLength = pContentLength;
}
/**
* Return Json style string
* @return
*/
public String getJsonStyleStatus() {
StringBuffer buf = new StringBuffer();
buf.append("{bytesRead:").append(this.bytesRead).
append(",contentLength:").append(this.contentLength).append('}');
return buf.toString();
}
}
<script type="text/javascript">
//Set the mainForm property
var mainForm=Ext.getDom('mainForm');
mainForm.enctype='multipart/form-data';
//==== Progress bar ====
var uploadProgressBar = new Ext.ProgressBar({
text:'Uploading ...'
});
Ext.onReady(function(){
});
//Start upload
function startUpload() {
//FIXME add validation later.
Ext.Ajax.timeout = 1000*3600;
Ext.Ajax.request({
method:'post',
form: 'mainForm',
params:'method=upload',
url: 'uploadImageHandler.html',
success: uploadSuccess,
failure: uploadFail
});
if (!uploadProgressBar.rendered) uploadProgressBar.render('uploadProgressBarContainer');
uploadProgressBar.updateProgress(0,'Start uploading...');
//Start Timer thread
Ext.TaskMgr.start({
run:function(){
Ext.Ajax.request({
method:'post',
params:'method=queryStatus',
url: 'uploadImageHandler.html',
success: queryUploadSuccess,
failure: queryUploadFail
});
},
interval: 1000
});
}
function uploadSuccess(response){
alert(response.responseText + " upload is successful!");
}
function uploadFail(response) {
alert("Upload image is failed.");
Ext.TaskMgr.stopAll();
}
function queryUploadSuccess(response) {
//alert(response.responseText + " query success!");
var fileStatus = Ext.decode(response.responseText);
var percentage=fileStatus.bytesRead/fileStatus.contentLength;
uploadProgressBar.updateProgress(percentage, (percentage * 100) + "%");
if (percentage == 1)
Ext.TaskMgr.stopAll();
}
function queryUploadFail(response) {
alert("Ajax call query status fails!");
Ext.TaskMgr.stopAll();
}
</script>
很粗的一个测试例子, 好好完善Cancel之类的按钮也是可以加上去的。网上也有很多相似的文章, 本文主要是
在Spring MVC那个专有些, 使用Ext 1.1.1 + 2.0的progress bar widget, 做页面是个苦事.