springMVC上传文件 带有进度条

来源:互联网 发布:三维软件开发平台 编辑:程序博客网 时间:2024/05/16 23:48

     springMVC上传文件 带有进度条



一、需求

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

二、实现方式

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

三、遇到的问题

         采用以上方式,会有比较大的问题。
在监听器那边采用的存到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.非IEif(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 {//维护文件标识idpublic 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() {returnuploadKeys.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() {returnresMap.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 )@ResponseBodypublic 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 0