JavaWeb 使用ajax上传文件并显示进度条等上传信息
文件上传在Web程序中是常用的功能,一般上传图片或者文件大小比较少的文件客户端不需要显示上传进度或文件上传的信息,如果是上传的文件比较大的时候,可以在客户端展示文件上传进度以及根据业务的需求显示文件上传的各种信息。
在JavaWeb中使用ajax技术实现带有进度条的文件上传,实现的方法有多种,大概逻辑分为:当客户端上传文件发送请求后,服务器将上传的文件写入某路径中,在此上传的过程中,将文件上传的各种信息保存或输出到客户端,客户端间接性不断发送请求来获取上传文件的信息,从而达到文件上传时实时显示进度条等信息。
(1)、jsp页面代码(一个表单用于文件上传以及显示文件上传的进度条、一个table显示文件上传时的信息,from表单属性设置为multipart/form-data,设置一个隐藏的iframe,form的target等同于iframe的name),下面是html的部分代码
(2)、JS代码(通过ajax获取上传进度的信息),
function createXMLHttpRequest(){//创建ajax对象
try{return new XMLHttpRequest();
}catch(e){
try{ return new ActiveXObject("Msxm12.XMLHTTP");//IE6.0
}catch(e){
try{
return new ActiveXObject("Microsoft.XMLHTTP");//IE5.5以及更早版本
}catch(e){
alert("该浏览器不支持ajax");
throw e; }} }}
var completionStatus = true; //上传是否结束
function $(obj){
return document.getElementById(obj);
}
表单提交调用此方法,设置该completionStatus变量为false,显示进度条,为了防止重复提交,将按钮的disable属性设为true,使用setTimeout()方法每0.5秒调用requestStatus请求方法
function showStatus(){
completionStatus = false;
$('status').style.display = 'block'; // 将隐藏的进度条显示
$('btnSubmit').disabled = true; // 避免重复提交 setTimeout("requestStatus()", 500);
}
使用ajax获取上传文件的进度信息,先得到XMLHttpRequest对象,设置请求路径以及请求方式打开与服务器的连接,然后再发送请求,使用setTimeout方法每0.5秒重新发送一次请求从而动态更新进度条等上传信息。
function requestStatus(){// 向服务器请求上传进度信息
if(completionStatus) return;
var req = createXMLHttpRequest(); // 获取Ajax 请求对象
req.open("GET","${path}/UploadServlet"); // 设置请求路径
req.onreadystatechange = function(){
callback(req); // 请求完毕就执行 回调函数
}
req.send(null);//发送请求,如果不设置为null可能造成部分浏览器无法发送
setTimeout("requestStatus()",500); // 0.5秒后重新发送请求
}
function callback(req){ //回调函数,
var element;
if(req.readyState == 4){ //响应完成
if(req.status != 200) {//请求不成功
alert("发生错误 错误状态码:"+req.status+" ");//弹出错误码
return;}
var data = req.responseText.split(":"); //对传过来的data进行分割处理
layui.use('element', function(){
element = layui.element; }); //动态操作进度条,依赖element模块
element.progress('demo', data[0]+'%');//更新进度条
$('complete').innerHTML=data[1];//已完成数
$('length').innerHTML=data[2];//文件总长度
$('transmission').innerHTML=data[3];//传输速率
$('ElapsedTime').innerHTML=data[4];//已用时间
$('scheduledTime').innerHTML=data[5];//预计时间
$('EstimateRemainingTime').innerHTML=data[6];//预计剩余时间
if(data[1] == data[2]){//当已完成数等于文件总长度
completionStatus = true;
$('state').innerHTML ="上传完成";
$('btnSubmit'). disabled = false; //设置按钮可点击}}}
UploadInfo.java部分代码(封装文件上传信息到JavaBean,用于保存上传进度信息),省略了部分代码
上传监听器(UploadListener.java) 编写一个监听器,实现commons-fileupload组件的ProgressListrner接口,实现该接口后每次上传文件时会不断回调该方法然后传回上传数,将上传时的数据保存到UploadInfo这个JavaBean中。
public class UpoadListener implements ProgressListener {
private UploadInfo info;
public UpoadListener(UploadInfo info) { // 参数为UploadInfo的构造器
super();
this.info = info; // 将传过来的info保存下来
}
@Override
public void update(long bytesRead, long length, int items) {
info.setBytesRead(bytesRead); // 保存信息
info.setContentLength(length);
}
}
(3)、UploadServlet.java Post方法:上传文件使用的是POST提交,将上传信息保存到Session域中,设置监听器,对提交的表单进行解析,如果是上传的文件,将其输出保存到服务器项目上的uploadFile文件夹中
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
UploadInfo info = new UploadInfo(); // 上传信息
UpoadListener listener = new UpoadListener(info); // 上传文件监听器
req.getSession().setAttribute("uploadInfo", info); //保存到session域中
File file = null; // 定义上传File
ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory()); // 解析
upload.setProgressListener(listener); // 设置监听器
try {
List<FileItem> itemList = upload.parseRequest(req);
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"));
req.getSession().setAttribute("fileName", remoteFile.getName());
if(!remoteFile.getName().equals("")) { // 上传的文件不为空
// 创建一个和上传文件名称一样的文件
String path = this.getServletContext().getRealPath("uploadFile");
file = new File(path,remoteFile.getName());
file.getParentFile().mkdirs(); // 创建文件夹路径
file.createNewFile(); // 创建新文件
InputStream in = item.getInputStream(); // 将二进制数据输出到文件中
OutputStream out = new FileOutputStream(file); //写入到创建的文件
try {
byte[] bytes = new byte[1024];
int n ;
while((n = in.read(bytes)) != -1) {
out.write(bytes, 0, n); //遍历输出
}
}finally {
in.close();//关闭流
out.close();
} } } }
} catch (FileUploadException e) {
e.printStackTrace();
}
}
get方法:浏览器通过ajax请求方法获取上传进度信息,设置响应头告知浏览器不要缓存获取已经保存到session域中的uploadInfo(上传信息),从而计算出上传信息(如已传输事件,估计剩余时间、文件总长度、估计总时间等)
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("UTF-8");
resp.setHeader("Cache-Control", "no-store");// 设置响应头 禁止浏览器不缓存
resp.setHeader("Pragrma", "no-cache");
resp.setDateHeader("Expires", 0);
UploadInfo status = (UploadInfo) req.getSession().getAttribute("uploadInfo");
if(status == null) {
resp.getWriter().println("没有文件上传的信息");
return ;
}
long startTime = status.getStartTime();
long currentTime = System.currentTimeMillis();//返回以毫秒为单位的当前时间
long time = (currentTime - startTime)/1000+1 ; // 已传输的时间 单位:秒
// 传输速度 ;单位:byte/秒
double velocity =((double) status.getBytesRead())/(double)time; 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 StringBuffer sb = new StringBuffer();
sb.append(percent+":"+length+":"+totalLength+":"+velocity+":"+time+":"+totalTime+":"+timeLeft+":"+status.getItem());//拼接上传信息传递到jsp中
resp.getWriter().println(sb.toString());
resp.getWriter().flush();
resp.getWriter().close();
}
效果图