http://blog.csdn.net/tom_221x/article/details/3777064 网页
先看图:
csdn不给传,气啊!不传了。
原理:
使用了servlet,2个iframe,commons-fileupload的组件。2个iframe比较传统一个是上传文件数据,一个是更新页面进度条的。因为如果文件超大的话,在文件数据没有传完之前是不能回写数据的,所以一个iframe不行的。我这里没有使用session保存进度数据,我在网上看到不少的实现都是用session,最近在看rest觉得session不太符合rest风格。
我是利用线程+同步hashmap是实现的。 具体是这样的,页面2个iframe首次的请求不是传文件请求,利用一个iframe的src属性请求到servlet没有什么参数.这个iframe也是以后更新页面进度条的iframe.在这次请求中,构造进度条监听器,产生一个唯一的字符串作为key,映射到这个监听器对象。然后呢,把利用iframe push js脚本调用客户端的另一个iframe上传文件,并且把key发回去作为参数传回来。这时候要阻塞当前线程利用循环就可以了。 第二个上传线程来的时候,获取key,从同步hashmap中得到监听器,把监听器给上传线程的commons-fileupload的对象。这样两个线程就能通过监听器对象通信进度数据了。最后在阻塞的那个iframe中不断的push脚本更新页面就可以了。
测试了ie,firefox,chrome的都可以除了ie有点呆,多线程也测了ok的。下面看代码,很简单就一个页面一个servlet。
- <%@ page language="java" pageEncoding="utf-8"%>
- <%
- String path = request.getContextPath();
- String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
- %>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <base href="<%=basePath%>">
- <title>ajax显示上传文件进度</title>
- <meta http-equiv="pragma" content="no-cache">
- <meta http-equiv="cache-control" content="no-cache">
- <meta http-equiv="expires" content="0">
- <!--
- <link rel="stylesheet" type="text/css" href="styles.css" mce_href="styles.css">
- -->
- <mce:style type="text/css"><!--
- iframe{
- border:0;
- }
- #p_out{
- width:200px;
- height:12px;
- margin:10px 0 0 0;
- padding:1px 1px 1px 1px;
- font-size:10px;
- border:solid #d4e4ff 1px;
- }
- #p_in{
- width:0%;
- height:100%;
- background-color:#d4e4ff;
- margin:0;
- padding:0;
- }
- #dis{
- margin:0;
- padding:0;
- text-align:center;
- font-size:12px;
- height:12px;
- width:200px;
- }
- --></mce:style><mce:style type="text/css" mce_bogus="1"><!--
- iframe{
- border:0;
- }
- #p_out{
- width:200px;
- height:12px;
- margin:10px 0 0 0;
- padding:1px 1px 1px 1px;
- font-size:10px;
- border:solid #d4e4ff 1px;
- }
- #p_in{
- width:0%;
- height:100%;
- background-color:#d4e4ff;
- margin:0;
- padding:0;
- }
- #dis{
- margin:0;
- padding:0;
- text-align:center;
- font-size:12px;
- height:12px;
- width:200px;
- }
- --></mce:style><style type="text/css" mce_bogus="1" mce_bogus="1"> iframe{
- border:0;
- }
- #p_out{
- width:200px;
- height:12px;
- margin:10px 0 0 0;
- padding:1px 1px 1px 1px;
- font-size:10px;
- border:solid #d4e4ff 1px;
- }
- #p_in{
- width:0%;
- height:100%;
- background-color:#d4e4ff;
- margin:0;
- padding:0;
- }
- #dis{
- margin:0;
- padding:0;
- text-align:center;
- font-size:12px;
- height:12px;
- width:200px;
- }
- </style>
- </head>
- <body>
- <form id="uploadfile_form" name="uploadfile_form" enctype="multipart/form-data"
- method="post" target="uploadfile_iframe">
- <input type="file" name="file" />
- <br><br>
- <button onclick="progress()">提交</button>
- <div id="p_out"><div id="p_in"></div></div>
- <div id="dis"></div>
- </form>
- <iframe width="0px" height="0px" id="uploadfile_iframe" name="uploadfile_iframe" src="javascript:void(0)" mce_src="javascript:void(0)"></iframe>
- <iframe width="0px" height="0px" id="progress_iframe" name="progress_iframe" src="javascript:void(0)" mce_src="javascript:void(0)"></iframe>
- </body>
- <mce:script type="text/javascript"><!--
- function progress(){
- document.getElementById('progress_iframe').src = 'UploadFile';
- document.getElementById('dis').innerHTML = '初始化数据...';
- }
- function uploadFile(k){
- document.forms[0].action = 'UploadFile?key='+k;
- document.forms[0].submit();
- }
- function upload(len,total){
- document.getElementById('p_in').style.width = (Math.round(len/total*100))+'%';
- document.getElementById('dis').innerHTML = len+'/'+total+' Byte';
- }
- // --></mce:script>
- </html>
- package com.scott.uploadfile;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.PrintWriter;
- import java.net.URLEncoder;
- import java.util.Iterator;
- import java.util.List;
- import java.util.concurrent.ConcurrentHashMap;
- import javax.servlet.ServletException;
- 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.ProgressListener;
- import org.apache.commons.fileupload.disk.DiskFileItemFactory;
- import org.apache.commons.fileupload.servlet.ServletFileUpload;
- import com.scott.log.Log;
- /**
- * @author scott.wanglei
- * @since 2009-1-8
- * 使用多线程实现文件进度的显示
- */
- public class UploadFile extends HttpServlet {
- private static final long serialVersionUID = 4030816613803833495L;
- //同步散列表保存文件上传进度类的引用
- private ConcurrentHashMap<String,Progress> chp = new ConcurrentHashMap<String,Progress>();
- public UploadFile() {
- super();
- }
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- this.doPost(request, response);
- }
- @SuppressWarnings("deprecation")
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- Log.info("客户端提交类型: "+request.getContentType());
- response.setContentType("text/html;charset=UTF-8");
- response.setHeader("pragma", "no-cache");
- response.setHeader("cache-control", "no-cache");
- response.setHeader("expires", "0");
- if(request.getContentType() == null){
- Progress p = new Progress();
- //根据上request对象产生的String的hashcode值做为key值
- String key = Integer.toString(request.toString().hashCode());
- Log.info("key1 = "+key);
- //根据key值储存当前文件的进度对象引用
- chp.put(key, p);
- //把key传回并发起上传文件的请求
- this.sendMsg(response,"parent.uploadFile('"+key+"')");
- //上传线程没有开始时候阻塞更新线程
- while(true){
- if(p.getLength() != )
- break;
- }
- long temp = ;
- while(! p.isComplete){
- if(temp != p.getCurrentLength()){
- temp = p.getCurrentLength();
- //更新客户端进度
- this.sendMsg(response, "parent.upload('"+temp+"','"+p.getLength()+"')");
- }
- }
- }
- else if(request.getContentType().indexOf("multipart/form-data") > -1){
- //上传线程开始执行
- String key = request.getParameter("key");
- Log.info("key2 = "+key);
- Progress p = chp.get(key);
- if(p != null){
- p.setLength(request.getContentLength());
- Log.info("文件长度: "+request.getContentLength());
- this.startUploadFile(request,response, p,request.getRealPath("/"),key);
- chp.remove(key);
- }
- else{
- Log.info("Progress is null,k1 is not equal k2 ");
- this.sendMsg(response, "alert('Progress is null,k1 is not equal k2')");
- }
- }
- else {
- Log.redInfo("ContentType is not correctly");
- this.sendMsg(response, "alert('ContentType is not correctly')");
- }
- }
- //上传文件
- @SuppressWarnings({ "deprecation", "unchecked"})
- private void startUploadFile( HttpServletRequest request, HttpServletResponse response,Progress p,
- String path,String key)
- throws IOException{
- //设置上传工厂
- DiskFileItemFactory factory = new DiskFileItemFactory();
- Log.info("上传临时路径: "+path);
- factory.setRepository(new File(path));
- //阀值,超过这个值才会写到临时目录
- factory.setSizeThreshold(1024*1024*10);
- ServletFileUpload upload = new ServletFileUpload(factory);
- //最大上传限制
- upload.setSizeMax(1024*1024*200);
- //设置监听器监听上传进度
- upload.setProgressListener(p);
- List<FileItem> items = null;
- try {
- items = upload.parseRequest(request);
- } catch (FileUploadException e) {
- e.printStackTrace();
- this.errorAndStorp(response, "alert('FileUploadExeception happened')", p,key);
- }
- for(Iterator<FileItem> it = items.iterator();it.hasNext();){
- FileItem item = it.next();
- if(item.isFormField()){
- //处理表单域
- }
- else{
- try {
- Log.info("上传路径: "+path+item.getName());
- FileOutputStream fos = new FileOutputStream(path+URLEncoder.encode(item.getName(),"utf-8"));
- if(item.isInMemory()){//文件全在内存中
- fos.write(item.get());
- p.setComplete(true);
- }
- else{
- InputStream is = item.getInputStream();
- byte[] buffer = new byte[1024];
- int len;
- while((len = is.read(buffer)) > ){
- fos.write(buffer, , len);
- }
- p.setComplete(true);
- is.close();
- fos.close();
- }
- } catch (Exception e) {
- e.printStackTrace();
- this.errorAndStorp(response, "alert('Exception happened')", p,key);}
- }
- }
- }
- //上传线程一旦出现意外,就让progress出于完成状态以终止向客户端发送信息的线程
- private void errorAndStorp(HttpServletResponse response,String script,
- Progress p,String key)
- throws IOException{
- p.setComplete(true);
- chp.remove(key);
- this.sendMsg(response, script);
- }
- //向客户端发送数据
- private void sendMsg(HttpServletResponse response,String script) throws IOException{
- PrintWriter out = response.getWriter();
- out.println("<mce:script type='text/javascript'><!--
- "+script+"
- // --></mce:script>");
- //ie太拽了发送的字节少了它根本不掉你
- out.println("---------------------------------------------------");
- out.println("---------------------------------------------------");
- out.println("---------------------------------------------------");
- out.println("---------------------------------------------------");
- out.flush();
- }
- //存放文件上传进度
- private static class Progress implements ProgressListener{
- private long length = ;//文件总长度
- private long currentLength = ;//已上传的文件长度
- private boolean isComplete = false;//上传是否完成
- //实现监听器方法实时更新进度属性
- public void update(long bytesRead, long contentLength, int items) {
- currentLength = bytesRead;
- }
- public Progress(){}
- public long getLength() {
- return length;
- }
- public void setLength(int l){
- this.length = l;
- }
- public long getCurrentLength() {
- return currentLength;
- }
- public void setCurrentLenght(int cl){
- this.currentLength = cl;
- }
- public boolean isComplete() {
- return isComplete;
- }
- public void setComplete(boolean isComplete) {
- this.isComplete = isComplete;
- }
- }
- }
总结:
1. 在ie中document.frames['name'].src 和windows.frames['name'].src的赋值都是没有权限的。只有用
document.getElementById(' ').src可以的。
2. 在servlet中response.getWriter().flush();是不能让IE做出反应的,除非response.getWriter().close().或者回
写的字节流要够多才行的。
3. form的enctype="multipart/form-data"的时候,如果在url后用?带参数的话,在后request,getParametes("")
还是能取到?后面带的参数的,并且from表单里没有这个参数。
4. 在servlet里缓存request和response充满了不确定性的,因为servlet的结束了request和resposne也发生了变
化。