基于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。其源码如下:
package com.ncu.upload;
import java.util.*;
public class FileUploadStatus {
//上传总量
private long uploadTotalSize=0;
//读取上传总量
private long readTotalSize=0;
//当前上传文件号
private int currentUploadFileNum=0;
//成功读取上传文件数
private int successUploadFileCount=0;
//状态
private String status="";
//处理起始时间
private long processStartTime=0l;
//处理终止时间
private long processEndTime=0l;
//处理执行时间
private long processRunningTime=0l;
//上传文件URL列表
private List uploadFileUrlList=new ArrayList();
//取消上传
private boolean cancel=false;
//上传base目录
private String baseDir="";
public String getBaseDir() {
return baseDir;
}
public void setBaseDir(String baseDir) {
this.baseDir = baseDir;
}
public boolean getCancel() {
return cancel;
}
public void setCancel(boolean cancel) {
this.cancel = cancel;
}
public List getUploadFileUrlList() {
return uploadFileUrlList;
}
public void setUploadFileUrlList(List uploadFileUrlList) {
this.uploadFileUrlList = uploadFileUrlList;
}
public long getProcessRunningTime() {
return processRunningTime;
}
public void setProcessRunningTime(long processRunningTime) {
this.processRunningTime = processRunningTime;
}
public long getProcessEndTime() {
return processEndTime;
}
public void setProcessEndTime(long processEndTime) {
this.processEndTime = processEndTime;
}
public long getProcessStartTime() {
return processStartTime;
}
public void setProcessStartTime(long processStartTime) {
this.processStartTime = processStartTime;
}
public long getReadTotalSize() {
return readTotalSize;
}
public void setReadTotalSize(long readTotalSize) {
this.readTotalSize = readTotalSize;
}
public int getSuccessUploadFileCount() {
return successUploadFileCount;
}
public void setSuccessUploadFileCount(int successUploadFileCount) {
this.successUploadFileCount = successUploadFileCount;
}
public int getCurrentUploadFileNum() {
return currentUploadFileNum;
}
public void setCurrentUploadFileNum(int currentUploadFileNum) {
this.currentUploadFileNum = currentUploadFileNum;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public long getUploadTotalSize() {
return uploadTotalSize;
}
public void setUploadTotalSize(long uploadTotalSize) {
this.uploadTotalSize = uploadTotalSize;
}
}
package com.ncu.upload;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.ProgressListener;
public class UploadListener implements ProgressListener {
private HttpSession session=null;
public UploadListener (HttpSession session){
this.session=session;
}
/**
* 更新状态
* @param pBytesRead 读取字节总数
* @param pContentLength 数据总长度
* @param pItems 当前正在被读取的field号
*/
public void update(long pBytesRead, long pContentLength, int pItems) {
FileUploadStatus fuploadStatus = UploadServlet.takeOutFileUploadStatusBean(this.session);
fuploadStatus.setUploadTotalSize(pContentLength);
//读取完成
if (pContentLength == -1) {
fuploadStatus.setStatus("完成对" + pItems + "个文件的读取:读取了 " + pBytesRead + "/" + pContentLength+ " bytes.");
fuploadStatus.setReadTotalSize(pBytesRead);
fuploadStatus.setCurrentUploadFileNum(pItems);
fuploadStatus.setProcessEndTime(System.currentTimeMillis());
fuploadStatus.setProcessRunningTime(fuploadStatus.getProcessEndTime());
}else{//读取过程中
fuploadStatus.setStatus("当前正在处理第" + pItems+"个文件:已经读取了 " + pBytesRead + " / " + pContentLength+ " bytes.");
fuploadStatus.setReadTotalSize(pBytesRead);
fuploadStatus.setCurrentUploadFileNum(pItems);
fuploadStatus.setProcessRunningTime(System.currentTimeMillis());
}
//System.out.println("已经读取:" + pBytesRead);
UploadServlet.storeFileUploadStatusBean(this.session, fuploadStatus);
}
}
package com.ncu.upload;
import java.io.*;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.*;
/**
* Servlet implementation class for Servlet: UploadServlet
*
*/
public class UploadServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
static final long serialVersionUID = 1L;
public static final String UPLOAD_STATUS="UPLOAD_STATUS";
public static final String UPLOAD_DIR="/upload";
public UploadServlet() {
super();
}
/**
* 从文件路径中取出文件名
* @param filePath
* @return
*/
private String takeOutFileName(String filePath){
int pos=filePath.lastIndexOf(File.separator);
if (pos>0){
return filePath.substring(pos+1);
}
else{
return filePath;
}
}
/**
* 从request中取出FileUploadStatus Bean
* @param request
* @return
*/
public static FileUploadStatus takeOutFileUploadStatusBean(HttpSession session){
Object obj=session.getAttribute(UPLOAD_STATUS);
if (obj!=null){
return (FileUploadStatus)obj;
}
else{
return null;
}
}
/**
* 把FileUploadStatus Bean保存到session
* @param request
* @param uploadStatusBean
*/
public static void storeFileUploadStatusBean(
HttpSession session,
FileUploadStatus uploadStatusBean){
session.setAttribute(UPLOAD_STATUS,uploadStatusBean);
}
/**
* 删除已经上传的文件
* @param request
*/
private void deleteUploadedFile(HttpServletRequest request){
FileUploadStatus fUploadStatus=takeOutFileUploadStatusBean(request.getSession());
for(int i=0;i0){
String fileName=takeOutFileName(item.getName());
File uploadedFile = new File(request.getRealPath(UPLOAD_DIR)+File.separator+fileName);
item.write(uploadedFile);
//更新上传文件列表
FileUploadStatus fUploadStatus=takeOutFileUploadStatusBean(request.getSession());
fUploadStatus.getUploadFileUrlList().add(fileName);
storeFileUploadStatusBean(request.getSession(),fUploadStatus);
Thread.sleep(500);
}
}
} catch (FileUploadException e) {
e.printStackTrace();
//uploadExceptionHandle(request,"上传文件时发生错误:"+e.getMessage());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
//uploadExceptionHandle(request,"保存上传文件时发生错误:"+e.getMessage());
}
}
/**
* 回应上传状态查询
* @param request
* @param response
* @throws IOException
*/
private void responseFileUploadStatusPoll(HttpServletRequest request,HttpServletResponse response) throws IOException{
FileUploadStatus fUploadStatus=(FileUploadStatus)request.getSession().getAttribute(UPLOAD_STATUS);
//计算上传完成的百分比
long percentComplete = (long)Math.floor(((double) fUploadStatus.getReadTotalSize()/(double) fUploadStatus.getUploadTotalSize())*100.0);
System.out.println("com:"+percentComplete);
response.setContentType("text/xml");
response.setCharacterEncoding("UTF-8");
response.setHeader("Cache-Control", "no-cache");
if ( ((long)fUploadStatus.getReadTotalSize() == (long)fUploadStatus.getUploadTotalSize()) || (fUploadStatus.getCancel() == true)){
response.getWriter().write(fUploadStatus.getStatus().toString()+"success");
}else{
response.getWriter().write(fUploadStatus.getStatus().toString()+"");
}
}
/**
* 处理取消文件上传
* @param request
* @param response
* @throws IOException
*/
private void processCancelFileUpload(HttpServletRequest request,HttpServletResponse response) throws IOException{
FileUploadStatus fUploadStatus=(FileUploadStatus)request.getSession().getAttribute(UPLOAD_STATUS);
fUploadStatus.setCancel(true);
request.getSession().setAttribute(UPLOAD_STATUS, fUploadStatus);
responseFileUploadStatusPoll(request,response);
}
/**
* 在上传文件列表中查找与文件名相关的id
* @param request
* @param fileName 文件名
* @return 找到返回id,否则返回-1
*/
private int findFileIdInFileUploadedList(HttpServletRequest request,String fileName){
FileUploadStatus fileUploadStatus=takeOutFileUploadStatusBean(request.getSession());
for(int i=0;i
至此,服务器端的代码已经基本完成。
5. 客户端实现
由于在上传文件时需要在同一页面显示对应的进度条控件,因此,在提交表单时当前页面不能被刷新。我们可以通过将表单提交至一个隐藏的 iframe 中来实现。关于Ajax的技术前面讲过,这里就不再细说,直接给出源代码如下:
基于Ajax的上传文件显示进度条
至此,整个文件上传的实现到此完成,读者可以在此基础上,发挥自己的创新能力,去完善此实例。
Good Luck!
http://plkong.iteye.com/blog/238159