springMVC上传文件 带有进度条

未分类 一条评论

     springMVC上传文件 带有进度条

一、需求

               一个文件上传的功能(带进度条)

二、实现方式

              文件上传应该大部分人都接触过,一般都是基于commons-fileupload组件来实现,SpringMVC的文件上传功能也是在commons-fileupload组件提供的功能上面做了一些包装功能,使文件上传开发更容易方便。

三、遇到的问题

         采用以上方式,会有比较大的问题。
在监听器那边采用的存到session中,那么
                                     如果是同一个浏览器同一个标签页有同时触发了两次上传的操作,那么进度条的数值会乱串!
                                     如果是同一个浏览器不同标签页同时上传,同样进度条的数值也会乱串!!!
      

四、改进方式

       总的来说,在提交的时候,对上传任务进行编号,js端以时间戳加随机数编号就可以,把编号值传入服务端。在服务端session中对上传进度和编号进行一对一放入。获取的时候对比编号找出自己的那个上传任务,然后展示进度条就ok了。
        
      详细点简单点的说就是将一次请求上传文件看做是一个线程,那么一次上传文件的线程id是唯一的,通过维护一个静态的ConcurrentHashMap值,就可以在监听器那边获取到客户端这边传入的文件编号。

五、附录代码

     1.进度条代码(比较简单的,采用两个div,其中一个div作为背景,另一个div作为进度展示的宽度)
      

<html> 
<head> 
<title>进度条</title> 
<style type="text/css">  
.container{  
   width:450px;  
   border:1px solid #6C9C2C;  
   height:25px;  
 }
#bar{  
   background:#95CA0D;  
   float:left; 
   height:100%;  
   text-align:center;  
   line-height:150%; 
 }  
</style>  
<script type="text/javascript">  
  function run(){  
        var bar = document.getElementById("bar"); 
        var total = document.getElementById("total"); 
    bar.style.width=parseInt(bar.style.width) + 1 + "%";  
    //total.innerHTML = bar.style.width; 
    document.getElementById("bar1").innerHTML = bar.style.width; 
    if(bar.style.width == "100%"){  
      window.clearTimeout(timeout); 
      return; 
    } 
    var timeout=window.setTimeout("run()",100); 
  } 
    window.onload = function(){  
       run(); 
    }  
</script> 
  
</head> 
<body> 
  <div class="container"> 
  	<div id="bar1" style="  position: fixed;width:450px;text-align:center"></div>
   <div id="bar" style="width:0%;"></div>  
  </div>  
  <span id="total"></span> 
</body> 
</html>

2.js 计算上传文件的大小

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">  
<html xmlns="http://www.w3.org/1999/xhtml">  
    <head>  
    <meta name="DEscription" contect="my code demo" />  
    <meta name="Author" contect="Michael@www.micmiu.com" />  
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  
    <title>js验证文件大小</title>  
    </head>  
    <body>  
        <img id="tempimg" dynsrc="" src="" style="display:none" />  
        <input type="file" name="file" id="fileuploade" size="40" />  
        <input type="button" name ="check" value="checkfilesize" onclick="checkfile()"/>  
  
    </body>  
    <script type="text/javascript">  
        var maxsize = 2*1024*1024;//2M  
        var errMsg = "上传的附件文件不能超过2M!!!";  
        var tipMsg = "您的浏览器暂不支持计算上传文件的大小,确保上传文件不要超过2M,建议使用IE、FireFox、Chrome浏览器。";  
       
        function checkfile(){  
            try{  
                var obj_file = document.getElementById("fileuploade");  
                if(obj_file.value==""){  
                    alert("请先选择上传文件");  
                    return;  
                }  
                var filesize = 0;  
				//1.非IE
				if(obj_file.files != null && typeof(obj_file.files)!="undefined"){
						filesize = obj_file.files[0].size;  
						alert("非IE内核:"+filesize);
				}else{
				//2.IE
				  var obj_img = document.getElementById('tempimg');  
                    obj_img.dynsrc=obj_file.value;  
                    filesize = obj_img.fileSize;  
					alert("IE内核:"+filesize);
				}
				if(typeof(filesize) != 'number'){
					alert(tipMsg);
					  return;  
				}
                      alert("类型typeof:"+typeof(filesize));
               
                if(filesize==-1){  
                    alert(tipMsg);  
                    return;  
                }else if(filesize>maxsize){  
                    alert(errMsg);  
                    return;  
                }else{  
                    alert("文件大小符合要求");  
                    return;  
                }  
            }catch(e){  
                alert(e);  
            }  
        }  
 
    </script>  
</html>  

3.服务端改进的监听器代码:

    01.新建GlobalThreadMap类,该类主要维护文件上传 的文件标识 (主要通过线程id)注意,在上传文件完成之后需要在上传文件的action中需要执行两个remove方法。
public class GlobalThreadMap {
	//维护文件标识id
	public final static ConcurrentHashMap<Long, String> uploadKeys = new ConcurrentHashMap<Long, String>();
	//维护session 中进度条数值
	public static  ConcurrentHashMap<String, Progress>  resMap = new ConcurrentHashMap<String, Progress>();
	
	public static void putKey(String uploadKey) {
		long id = Thread.currentThread().getId();
		uploadKeys.put(id, uploadKey);
		System.out.println("GlobalThreadMap  putKey:" + id + "  " + uploadKey);
		System.out.println("当前GlobalThreadMap   uploadKeys的值是:     "+uploadKeys);
	}

	public static String getKey() {
		return	uploadKeys.get(Thread.currentThread().getId());
	}

	public static void removeKey() {
		uploadKeys.remove(Thread.currentThread().getId());
	}
	
	public static void putResValue(Progress progress) {
		resMap.put(getKey(), progress);
	}

	public static Progress getResValueByKey() {
		return	resMap.get(getKey());
	}

	public static void removeResKey() {
		resMap.remove(getKey());
	}

}

     02.在扩展的监听器的类CustomMultipartResolver中方法resolveMultipart中:

   public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {  
        // 获取到request,要用到session  
        this.request = request;
        //设置值
        String random = request.getParameter("random");
        GlobalThreadMap.putKey(random);
        return super.resolveMultipart(request);  
    }

03.在具体监听器实现这边FileUploadProgressListener

public class FileUploadProgressListener implements ProgressListener {
	private HttpSession session;

	public FileUploadProgressListener() {
	}

	public FileUploadProgressListener(HttpSession session) {
		this.session = session;
		Progress status = new Progress();
		// 设置值
		GlobalThreadMap.putResValue(status);
		session.setAttribute("upload_ps", GlobalThreadMap.resMap);// 这是现在的
																	// 但是key去取出来是一个空的值,我是在上传文件开始的地方设置的值
		// session.setAttribute("upload_ps", status); //这是之前的将进度条的数值设置到session里面
	}

	/**
	 * pBytesRead 到目前为止读取文件的比特数 pContentLength 文件总大小 pItems 目前正在读取第几个文件
	 */
	public void update(long pBytesRead, long pContentLength, int pItems) {
		String key = GlobalThreadMap.getKey();
		Map<String, Progress> map = (Map<String, Progress>) session
				.getAttribute("upload_ps");
		Progress status = map.get(key);
		status.setBytesRead(pBytesRead);
		status.setContentLength(pContentLength);
		status.setItems(pItems);
		status.setPercent(status.getPercent());
		status.setSpeed(status.getSpeed());
		status.setMbRead(status.getMbRead());
		// 设置值
		GlobalThreadMap.putResValue(status);
		session.setAttribute("upload_ps", GlobalThreadMap.resMap);
	}

}

04.同样的,在实时获取进度条数值的这边的action中,也需要稍稍修改一点:

@RequestMapping(value = "/upfile/progress.do", method = RequestMethod.POST )
	@ResponseBody
	public String initCreateInfo(HttpServletRequest request) {
		String random = request.getParameter("random");
		 Map<String, Progress> map =  (Map<String, Progress>) request.getSession().getAttribute("upload_ps");
		if(map == null ){
			return "{}";
		}
		Progress status = map.get(random);
		JSONObject jsonStu = JSONObject.fromObject(status);
		return jsonStu.toString();
	}

05.对了,xml配置那边,需要将上传文件的的监听器配置成多例的

<bean id="multipartResolver" scope="prototype"	class="com.sunsharing.ihome.air.web.common.fileupload.CustomMultipartResolver">
	    <property name="defaultEncoding" value="UTF-8" />
	    <property name="maxUploadSize" value="1000000000000" />
	</bean>

ok。改造完成。


====================================
====================================
====================================

1条评论

zs says: 回复

请问random是怎么设置的,request.getParameter(“random”); 这个获取出来时null啊

发表评论

电子邮件地址不会被公开。 必填项已用*标注

昵称 *