需要的额外jar包有 commons-fileupload-1.2.2.jar + commons-io-2.0.1.jar
最终效果图:
原理: 服务器在处理上传文件的同时,将上传的进度信息列如文件总长度,已上传多少等写入 Session 中。客户端浏览器利用 Ajax 技术再从 Session 中获取上传进度信息,并实时显示。
分别介绍实现带进度条的文件上传所需要的类,并附上各个类的全部代码。
1、封装上传数据的进度信息(封装到Java Bean 中):UploadStatus.java
普通的Java Bean 对象,用来保存进度信息。
package cn.joker.bean;
public class UploadStatus {
private long bytesRead; //已上传的字节数
private long contentLength; //所有文件的总长度
private int item; //正在上传第几个文件
private long startTime = System.currentTimeMillis(); // 开始上传的时间,用于计算速率
public long getBytesRead() {
return bytesRead;
}
public void setBytesRead(long bytesRead) {
this.bytesRead = bytesRead;
}
public long getContentLength() {
return contentLength;
}
public void setContentLength(long contentLength) {
this.contentLength = contentLength;
}
public int getItem() {
return item;
}
public void setItem(int item) {
this.item = item;
}
public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
}
2 、上传监听器:UploadListener.java
commons-fileupload 1.2版本支持上传监听,只需要实现一个监听器,并把它添加到上传组件上即可。监听器需要实现它的 ProgressListener 接口。
添加该监听器后,上传组件在上传文件时会不断地回调该方法,回传这些数据。我们利用这个监听器将上传的数据封装到 UploadStatus 中。然后将这个Bean 保存到 Session 中。
/**
*
*/
package cn.joker.listener;
import org.apache.commons.fileupload.ProgressListener;
import cn.joker.bean.UploadStatus;
/**
*
*
*/
public class UploadListener implements ProgressListener {
/* (non-Javadoc)
* @see org.apache.commons.fileupload.ProgressListener#update(long, long, int)
*/
private UploadStatus status; // 记录上传信息的Bean
public UploadListener(UploadStatus status) {
this.status = status;
}
@Override
public void update(long bytesRead, long contentLength, int item) {
// TODO Auto-generated method stub 上传组件会调用这个方法
status.setBytesRead(bytesRead);
status.setContentLength(contentLength);
status.setItem(item);
}
}
3、监听上传的进度:ProgressUploadServlet.java
监听上传过程需要为ServletFileUpload (上传组件)安装一个监听器(UploadListener.java),然后把存有上传进度信息的 UploadStatus 对象放入 Session 中。上传文件使用的是Post 方法,客户端浏览器通过Ajax 获取进度信息使用的是 Get 方法,所以在两个方法中分别写上各自代码。
package cn.joker.servlet;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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.ServletFileUpload;
import cn.joker.bean.UploadStatus;
import cn.joker.listener.UploadListener;
/**
* Servlet implementation class ProgressUploadServlet
*/
@WebServlet("/ProgressUploadServlet")
public class ProgressUploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public ProgressUploadServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//response.getWriter().append("Served at: ").append(request.getContextPath());
response.setCharacterEncoding("UTF-8");
// 禁止浏览器缓存
response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragrma", "no-cache");
response.setDateHeader("Expires", 0);
UploadStatus status = (UploadStatus) request.getSession().getAttribute("uploadStatus");
if(status == null) {
response.getWriter().println("没有上传信息");
return ;
}
long startTime = status.getStartTime();
long currentTime = System.currentTimeMillis();
long time = (currentTime - startTime)/1000+1 ; // 已传输的时间 单位:s
double velocity =((double) status.getBytesRead())/(double)time; // 传输速度 ;单位:byte/s
double totalTime = status.getContentLength()/velocity; //估计总时间
double timeLeft = totalTime - time ; // 估计剩余时间
int percent = (int)(100 * (double)status.getBytesRead()/(double)status.getContentLength()); // 已完成的百分比
double length = ((double)status.getBytesRead())/1024/1024; //已完成数;单位:M
double totalLength = ((double)status.getContentLength())/1024/1024; //总长度;单位:M
// 控制数据的输出格式
String value = percent+"||"+length+"||"+totalLength+"||"+velocity+"||"+time+"||"+totalTime+"||"+timeLeft+"||"+status.getItem();
//System.out.println(value);
// 输出给浏览器
response.getWriter().println(value);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
@SuppressWarnings("unchecked")
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
UploadStatus status = new UploadStatus(); // 上传状态
UploadListener listener = new UploadListener(status); // 监听器
request.getSession().setAttribute("uploadStatus", status); // 状态放入session 中
File file = null; // 上传的文件
ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory()); // 解析
upload.setProgressListener(listener); // 安装监听器
try {
List<FileItem> itemList = upload.parseRequest(request);
// 遍历所有参数
for(Iterator<FileItem> it = itemList.iterator();it.hasNext();) {
FileItem item = it.next();
// 如果是上传的文件
if(!item.isFormField()) {
File remoteFile = new File(new String(item.getName().getBytes(),"UTF-8"));
//System.out.println(remoteFile.getName());
//System.out.println(remoteFile.getAbsolutePath());
// 上传的文件不为空
if(!remoteFile.getName().equals("")) {
// 创建一个和上传文件名称一样的文件
//System.out.println(this.getServletContext().getRealPath("upload")); //上傳的路徑
file = new File(this.getServletContext().getRealPath("upload"),remoteFile.getName());
//System.out.println(file.getParentFile().getName());
file.getParentFile().mkdirs(); // 创建文件夹路径
file.createNewFile(); // 创建新文件
// 将二进制数据输出到文件中
InputStream ins = item.getInputStream(); // 上传的二进制数据
OutputStream ous = new FileOutputStream(file); // 写入创建的文件
try {
byte[] bytes = new byte[1024];
int n ;
while((n = ins.read(bytes)) != -1) {
ous.write(bytes, 0, n);
}
}finally {
ins.close();
ous.close();
}
}
}
}
} catch (FileUploadException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
4、上传文件的界面(包含通过Ajax获取上传进度信息): progressUpload.jsp
界面没作美观处理,功能已经实现。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<style type="text/css">
body,div{font-size:12px;font-familly:宋体;}
#progressBar {width:200px;height:12px;background:#FFFFFF;border:1px solid #000000;padding:1px;}
#progressBarItem {width:30%;height:100%;background:#FF0000;}
</style>
</head>
<body>
<iframe name="upload_iframe" width=0 height=0></iframe>
<form action="/ServletStudy/ProgressUploadServlet" method="post" enctype="multipart/form-data" target="upload_iframe" onsubmit="showStatus();">
<input type="file" name="file1" style="width:350px"> <br>
<input type="file" name="file2" style="width:350px"> <br>
<input type="file" name="file3" style="width:350px"> <br>
<input type="file" name="file4" style="width:350px"> <br>
<input type="submit" id="btnSubmit" value="上传文件">
</form>
<div id="status" style="display:none">
上传进度条:
<div id="progressBar"><div align="left" id="progressBarItem"></div></div>
<div id="statusInfo"></div>
</div>
<script type="text/javascript">
var finished = true; // 是否上传结束
function $(obj){
return document.getElementById(obj);
}
function showStatus(){
//alert("showStatus");
finished = false;
$('status').style.display = 'block'; // 将隐藏的进度条显示
$('progressBarItem').style.width = '1%'; // 设置进度条初始值
$('btnSubmit').disabled = true; // 防止重复提交
//alert("showStatus");
setTimeout("requestStatus()", 1000); // 1秒后执行 requestStatus 方法,更新上传进度
}
// 向服务器请求上传进度信息
function requestStatus(){
if(finished) return;
var req = createRequest(); // 获取Ajax 请求
req.open("GET","/ServletStudy/ProgressUploadServlet"); // 设置请求路径
req.onreadystatechange = function(){
callback(req); // 请求完毕就执行 callback(req)
}
req.send(null); //发送请求
//alert("requestStatus");
setTimeout("requestStatus()",1000); // 1秒后重新执行请求
}
// 返回Ajax 请求对象
function createRequest(){
if(window.XMLHttpRequest){
//alert("createRequest");
return new XMLHttpRequest();
}else {
try{
return new ActiveXObject("Msxm12.XMLHTTP");
}catch(e){
return new ActiveXObject("Microsoft.XMLHTTP");
}
}
return null;
}
function callback(req){
if(req.readyState == 4){ // 刷新进度条
if(req.status != 200) {
debug("发生错误 req.status"+req.status+" ");
return;
}
debug("status.jsp 返回值:"+ req.responseText);
var ss = req.responseText.split("||"); // 处理进度信息
$('progressBarItem').style.width = ''+ss[0]+'%';
$('statusInfo').innerHTML = '已完成百分比:'+ss[0]+'% <br /> 已完成数(M):'+ss[1]+'<br/>文件总长度(M):'
+ss[2]+'<br/> 传输速率(k):'+ss[3]+'<br/> 已用时间(s):'+ss[4]+'<br>估计总时间(s):'+ss[5]+
'<br> 估计剩余时间(S):'+ss[6]+'<br> 正在上传第几个文件:'+ss[7];
if(ss[1] == ss[2]){
finished = true;
$('statusInfo').innerHTML += "<br/><br/><br/>上传已完成。";
$('btnSubmit').disabled = false;
}
}
}
function debug(obj){
var div = document.createElement("DIV");
div.innerHTML = "[debug]: "+obj;
document.body.appendChild(div);
}
</script>
</body>
</html>