Servlet实现带进度条的文件上传
2016-12-16 20:35
323 查看
在上一篇博文中介绍了通过POST的方式上传文件:点击打开链接 ,这一篇文章将介绍如何实现带进度条的上传效果。
工作原理:上传文件的同时,Servlet将上传进度的信息例如文件总长度、已上传文件的多少、传输速率写入Session中。客户端浏览器利用Ajax技术再新开辟一个独立的线程冲Session中获取上传进度信息,并实时显示。Ajax可以不刷新页面获取服务器数据。Session可以看做服务器内存,可以用于存放少量客户信息。上传文件仍然使用Apache开源包:commons-file-1.2.jar和commons-io-2.5.jar包。
上传进度条:通过两个<div>实现:
效果图如下:
监听器:我们需要在文件上传的时候,不断的更新上传状态。commons-fileupload中有ProgressListener接口,只要实现这个接口中的方法:public void update(long bytesRead, long contentLength, int items)。byteRead表示已经上传的字节数,contentLength代表上传的文件的总长度,items表示正在上传第几个文件。添加了监听器之后,就可以计算出文件上传的进度,用进度条实时的显示出来。因此需要把这些数据保存起来,代码中通过一个自定义的Java
bean:UploadStatus类。代码如下:
监听上传进度:处理文件上传的Servlet类为ProgressUploadServlet。与上一篇博文中的UploadServlet类似,仍然使用ServletFileUpload类。监听上传过程需要安装一个监听器,然后把存有上传进度信息的UploadStatus对象放到Session中。上传文件是POST方法,因此需要把代码写到doPost()方法中:
读取上传进度:上传文件使用了doPost()方法,但是doGet()方法还没使用,所以可以通过doGet()方法来获取上传的进度信息。
显示上传进度:上传文件时,如果不对表单做特别处理,提交表单后会转到另一个页面,造成页面的刷新。而且新页面显示之前,浏览器会因等待而显示白屏。如果上传的文件很大,白屏时间会很长,因此需要对表单做一些特别处理,使提交后的表单内容不变,同时实时显示进度条,知道文件上传技术。
注意上传文件需要指定:enctype="multipart/form-data"。另外为表单添加了onsubmit="showStatus();"事件,提交表单后就会执行该函数,显示进度条,并用Ajax读取Session里边的上传进度,实时刷新进度条。showStatus的代码如下:
Ajax的核心是request,学名为XMLHttpRequest,需要浏览器支持。目前所有的主流浏览器都支持XMLHttpRequest。浏览器端的代码如下:progressUpload.jsp
运行完的效果图如下:
工作原理:上传文件的同时,Servlet将上传进度的信息例如文件总长度、已上传文件的多少、传输速率写入Session中。客户端浏览器利用Ajax技术再新开辟一个独立的线程冲Session中获取上传进度信息,并实时显示。Ajax可以不刷新页面获取服务器数据。Session可以看做服务器内存,可以用于存放少量客户信息。上传文件仍然使用Apache开源包:commons-file-1.2.jar和commons-io-2.5.jar包。
上传进度条:通过两个<div>实现:
<style type="text/css"> body,td,div{font-size:12px;font-familly:宋体;} #progressBar{width:400px;height:12px;background:#FFFFFF;border:1px solid #000000;padding:1px;} #progressBarItem{width:30%;height:100%;background:#FF0000;} </style> <div id="progressBar"><div id="progressBarItem"></div></div>
效果图如下:
监听器:我们需要在文件上传的时候,不断的更新上传状态。commons-fileupload中有ProgressListener接口,只要实现这个接口中的方法:public void update(long bytesRead, long contentLength, int items)。byteRead表示已经上传的字节数,contentLength代表上传的文件的总长度,items表示正在上传第几个文件。添加了监听器之后,就可以计算出文件上传的进度,用进度条实时的显示出来。因此需要把这些数据保存起来,代码中通过一个自定义的Java
bean:UploadStatus类。代码如下:
package com.hello.firstweb.listener; import org.apache.commons.fileupload.ProgressListener; import com.hello.firstweb.bean.UploadStatus; public class UploadListener implements ProgressListener{ private UploadStatus status; @Override public void update(long bytesRead, long contentLength, int items) { // TODO Auto-generated method stub status.setBytesRead(bytesRead);//已读取数据长度 status.setContentLength(contentLength);//文件总长度 status.setItems(items);//正在保存第几个文件 } public UploadListener(UploadStatus status){ this.status = status; } }
package com.hello.firstweb.bean; public class UploadStatus { private long bytesRead; //已上传的字节数 private long contentLength; //所有文件的总长度 private long startTime = System.currentTimeMillis(); // 开始上传时间,用于计算上传速率 private int items; //正在上传第几个文件 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 long getStartTime() { return startTime; } public void setStartTime(long startTime) { this.startTime = startTime; } public int getItems() { return items; } public void setItems(int items) { this.items = items; } }Upload中记录了上传开始的时间,用于计算上传速率、估计上传总时间等等。
监听上传进度:处理文件上传的Servlet类为ProgressUploadServlet。与上一篇博文中的UploadServlet类似,仍然使用ServletFileUpload类。监听上传过程需要安装一个监听器,然后把存有上传进度信息的UploadStatus对象放到Session中。上传文件是POST方法,因此需要把代码写到doPost()方法中:
package com.hello.firstweb; import com.hello.firstweb.bean.UploadStatus; import com.hello.firstweb.listener.UploadListener;
public class ProgressUploadServlet extends HttpServlet { // 上传配置 private static final int MEMORY_THRESHOLD = 1024 * 1024 * 3; // 3MB private static final int MAX_FILE_SIZE = 1024 * 1024 * 40; // 40MB private static final int MAX_REQUEST_SIZE = 1024 * 1024 * 50; // 50MB
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { UploadStatus status = new UploadStatus(); UploadListener listener = new UploadListener(status); request.getSession().setAttribute("uploadStatus",status); //创建解析工厂 DiskFileItemFactory factory = new DiskFileItemFactory(); factory.setRepository(new File(System.getProperty("java.io.tmpdir")));//设置文件缓存目录 //设置文件缓存目录 //创建解析器 ServletFileUpload upload = new ServletFileUpload(factory); // 设置最大文件上传值 upload.setFileSizeMax(MAX_FILE_SIZE); // 设置最大请求值 (包含文件和表单数据) upload.setSizeMax(MAX_REQUEST_SIZE); upload.setHeaderEncoding("UTF-8"); //解析request得到封装FileItem的list upload.setProgressListener(listener); try{ List<FileItem> list = upload.parseRequest(request); System.out.println("size is "+list.size()); for(FileItem item:list){ if(item.isFormField()){ //表单数据 System.out.println("FormField: "+item.getFieldName()+"="+item.getString()); }else{ //否则就是上传的文件 System.out.println("File:"+item.getName()); String fileName = item.getName(); //最好加上这个,因为有可能只上传一个文件,如果这样的话,其它文件名是 //空的,在写时就会认为它是一个目录而报错 if(fileName == null || "".equals(fileName)){ System.out.println("upload file is null"); continue; } File saved = new File("D:\\WebWorkspace\\upload_test",fileName); saved.getParentFile().mkdirs(); System.out.println("real path:" + saved.getAbsolutePath()); InputStream ins = item.getInputStream(); OutputStream ous = new FileOutputStream(saved); byte[] tmp = new byte[1024]; int len = -1; while((len = ins.read(tmp))!=-1){ ous.write(tmp,0,len); //写文件 } ous.close(); ins.close(); response.getWriter().println("已保存文件:"+saved); } } //把异常改了,这样才会打印准确报错 }catch(FileUploadException e){ System.out.println("Exception!!!!!!!!!!!!!"); System.out.println(e.getMessage()); response.getWriter().println("上传发生错误:"+e.getMessage()); } } }通过ProgressUploadServlet来处理上传文件,就能够获取上传的开始时间、已经上传的字节数、总字节数等等,不过这些信息仍然存放在Session里,需要再写一个程序读取。
读取上传进度:上传文件使用了doPost()方法,但是doGet()方法还没使用,所以可以通过doGet()方法来获取上传的进度信息。
package com.hello.firstweb;至此,使用快捷键Ctrl+N新开子窗口访问ProgressUploadServlet就可以看到文字版的上传仅需,不断的刷新,就能看到不断的更新,但是这样也太不智能了。所以我们还需要一个Ajax程序,自动获取该Servlet返回的数据,将数据显示到进度条上,并且每一秒钟自动更新一次。
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 com.hello.firstweb.bean.UploadStatus;
import com.hello.firstweb.listener.UploadListener;
public class ProgressUploadServlet extends HttpServlet { // 上传配置 private static final int MEMORY_THRESHOLD = 1024 * 1024 * 3; // 3MB private static final int MAX_FILE_SIZE = 1024 * 1024 * 40; // 40MB private static final int MAX_REQUEST_SIZE = 1024 * 1024 * 50; // 50MBpublic void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("Cache-Control", "no-store"); //禁止浏览器缓存
response.setHeader("Pragrma", "no-cache"); //禁止浏览器缓存
response.setDateHeader("Expires", 0); //禁止浏览器缓存
UploadStatus status = (UploadStatus) request.getSession(true).getAttribute("uploadStatus");//从session中读取上传信息
if(status == null){
response.getWriter().println("没有上传信息");
return;
}
long startTime = status.getStartTime(); //上传开始时间
long currentTime = System.currentTimeMillis(); //现在时间
long time = (currentTime - startTime) /1000 +1;//已经传顺的时间 单位:s
double velocity = status.getBytesRead()/time; //传输速度:byte/s
double totalTime = status.getContentLength()/velocity; //估计总时间
double timeLeft = totalTime -time; //估计剩余时间
int percent = (int)(100*(double)status.getBytesRead()/(double)status.getContentLength()); //百分比
double length = status.getBytesRead()/1024/1024; //已完成数
double totalLength = status.getContentLength()/1024/1024; //总长度 M
String value = percent+"||"+length+"||"+totalLength+"||"+velocity+"||"+time+"||" +totalTime
+"||"+timeLeft+"||"+status.getItems();
response.getWriter().println(value); //输出给浏览器进度条
}
}
显示上传进度:上传文件时,如果不对表单做特别处理,提交表单后会转到另一个页面,造成页面的刷新。而且新页面显示之前,浏览器会因等待而显示白屏。如果上传的文件很大,白屏时间会很长,因此需要对表单做一些特别处理,使提交后的表单内容不变,同时实时显示进度条,知道文件上传技术。
<iframe name=upload_iframe width=0 height=0></iframe> <form action="servlet/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="text" name="description2" class="text"> <input type="submit" value="开始上传" id="btnSubmit"> </form>target默认属性为_self。如果target为默认值,则提交后的新页面会在当前窗口显示,造成窗口短暂的白屏。通过在页面内添加一个隐藏的iframe,把target指定为该隐藏的iframe,则提交后的新页面会在iframe内显示,iframe会短暂的白屏,因为iframe是隐藏的,所以我们看不到变化。
注意上传文件需要指定:enctype="multipart/form-data"。另外为表单添加了onsubmit="showStatus();"事件,提交表单后就会执行该函数,显示进度条,并用Ajax读取Session里边的上传进度,实时刷新进度条。showStatus的代码如下:
<script type="text/javascript"> var finished = true; //上传是否结束 function $(obj){ return document.getElementById(obj); //快速返回id为obj的HTML对象 } function showStatus(){ finished = false; //显示进度条 $('status').style.display='block'; $('progressBarItem').style.width="1%"; //进度条初始为1% $('btnSubmit').disabled = true; //将提交按钮置灰 setTimeout("requestStatus()",1000);//1s钟后执行requestStatus()方法 } </script>Ajax能够不刷新页面而改变页面的内容,他的原理是创建一个request,用这个request获取其他页面的内容,并显示到页面上。因为它没有在浏览器中输入新的地址,所以页面不会有刷新,抖动。
Ajax的核心是request,学名为XMLHttpRequest,需要浏览器支持。目前所有的主流浏览器都支持XMLHttpRequest。浏览器端的代码如下:progressUpload.jsp
<%@ page language="java" import="java.util.*" contentType="text/html ;charset=UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'uploadProgress.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv ="Content-type" content="text/html ;charset=UTF-8">
<style type="text/css">
body,td,div{font-size:12px;font-familly:宋体;}
#progressBar{width:400px;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="servlet/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="text" name="description2" class="text"> <input type="submit" value="开始上传" id="btnSubmit"> </form>
<div id="status" style="display:none;">
上传进度条:
<div id="progressBar"><div id="progressBarItem"></div></div>
<div id="statusInfo"></div>
</div>
<script type="text/javascript">
var finished = true; //上传是否结束
function $(obj){
return document.getElementById(obj); //快速返回id为obj的HTML对象
}
function showStatus(){
finished = false; //显示进度条
$('status').style.display='block';
$('progressBarItem').style.width="1%"; //进度条初始为1%
$('btnSubmit').disabled = true; //将提交按钮置灰
setTimeout("requestStatus()",1000);//1s钟后执行requestStatus()方法
}
function requestStatus(){
if(finished) return;
var req = createRequest(); //获取Ajax请求
req.open("GET","servlet/ProgressUploadServlet"); //设置请求路径
req.onreadystatechange=function(){callback(req);} //请求完毕就执行callback
req.send(null); //发送请求
setTimeout("requestStatus()",1000); //1s后重新请求
}
function createRequest(){
if(new XMLHttpRequest()){ //Netscape浏览器
return new XMLHttpRequest();
}else{ //IE浏览器
try{
return new ActiveXObject("Msxml2.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);//显示debug信息
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>
运行完的效果图如下:
相关文章推荐
- Servlet + Ajax 实现带进度条的文件上传
- servlet、struts2、springmvc中实现带进度条的文件上传
- 使用servlet实现简易上传文件(带进度条版本)
- servlet、struts2、springmvc中实现带进度条的文件上传
- 用servlet实现带进度条的文件上传
- Ajax + Servlet 实现上传文件进度条显示
- AJAX与JSF实现带进度条的批量上传文件实例
- PHP上传文件进度条的实现详情
- AJAX与JSF实现带进度条的批量上传文件实例
- AJAX与JSF实现带进度条的批量上传文件实例
- AJAX与JSF实现带进度条的批量上传文件实例
- 转载tangl_99的Servlet 实现文件上传下载--自己的一点心得
- AJAX与JSF实现带进度条的批量上传文件实例
- 采用AJAX实现带进度条的文件上传(C#)
- AJAX与JSF实现带进度条的批量上传文件实例
- AJAX与JSF实现带进度条的批量上传文件实例
- AJAX与JSF实现带进度条的批量上传文件实例
- AJAX与JSF实现带进度条的批量上传文件实例
- AJAX与JSF实现带进度条的批量上传文件实例
- AJAX与JSF实现带进度条的批量上传文件实例