SSM框架+Plupload实现断点续传(Spring+SpringMVC+MyBatis+Plupload)

关于Plupload的介绍,相信它的官网http://www.plupload.com/已经给得很详细了。Plupload的上传原理简单点说,就是将用户选中的文件(可多个)分隔成一个个小块,依次向服务器上传,这是它能驾驭上传大文件的缘由之一,并且在这个过程能够暂停上传,暂停后再继续上传。最重要的是,从头至尾没有一点点UI阻塞,保证了用户体验。下面会开始讲Plupload的实现流程,分析原理,并在最后给出效果图。javascript


在此以前先说说个人项目,作的j2ee项目运用到Spring+SpringMVC+MyBatis的框架集合,是关于一个社交平台的网站,相似于facebook,twitter,微博等,起了一个名字叫YouAndMe。我大胆地构想了这个项目应该有一个用户资料共享的平台,或是一部好看的电影,或是一套电视剧,或是居家必备的食谱,也有多是好看的风景图,各式各样。用户能够搜索想要的资料并下载。所以首先要解决的就是各式各样(大)文件的上传。css


一:下载Plupload插件并引入相应文件html

  1. 值得一提的是这个插件只是前端的,后台怎么获取怎么将一个个小块合并等代码是要本身写的。前端

  2. 下载地址:http://www.plupload.com/download,我下的是Plupload 2.1.9 GPLv2版本的,里面有要用到的css以及js。html5

  3. 在项目中须要引入:jquery.plupload.queue.css,jquery-2.0.0.min.js,plupload.full.min.js,jquery.plupload.queue.js,zh_CN.js这些文件。java


二:前端准备jquery

1.首先在html中写入以下代码:web

<div id="uploader"> <p>Your browser doesn't have Flash, Silverlight or HTML5 support.</p> </div> <button id="toStop">暂停一下</button> <button id="toStart">再次开始</button>

注意div的id必须是uploader,这在插件源码里是有规定的;id为toStop与toStart的按钮是我本身加的,目的是为了实现暂停上传与暂停事后的继续上传。spring

2.页面加载后经过js初始化组件服务器

<script type="text/javascript"> $(function() { // Initialize the widget when the DOM is ready var uploader = $("#uploader").pluploadQueue({ // General settings runtimes: 'html5,flash,silverlight,html4', url: "../pluploadUpload", // Maximum file size max_file_size: '10000mb', chunk_size: '1mb', // Resize images on clientside if we can resize: { width: 200, height: 200, quality: 90, crop: true // crop to exact dimensions }, // Specify what files to browse for filters: [ {title: "Image files", extensions: "jpg,gif,png"}, {title: "Vedio files", extensions: "mp4,mkv"}, {title: "Zip files", extensions: "zip,avi"} ], // Rename files by clicking on their titles rename: true, // Sort files sortable: true, // Enable ability to drag'n'drop files onto the widget (currently only HTML5 supports that) dragdrop: true, // Views to activate views: { list: true, thumbs: true, // Show thumbs active: 'thumbs' }, // Flash settings flash_swf_url: 'js/Moxie.swf', // Silverlight settings silverlight_xap_url: 'js/Moxie.xap' }); $("#toStop").on('click', function () { uploader.stop(); }); $("#toStart").on('click', function () { uploader.start(); }); }); </script>

关于这部分的功能能够查看pluploadQueue的文档:http://www.plupload.com/docs/pluploadQueue。也很容易看懂,这里简单地说说部分参数的意义。

url就是服务器处理该上传的地址。filters是过滤器的意思,规定哪些格式的文件能够上传。dragdrop:true设置了能够拖拽文件至选定框。

注意:在暂停与继续上传时要用到uploader.stop()uploader.start(),这个uploader实例由$("#uploader").pluploadQueue({...})时产生。在官网给出的例子中有两种状况:一是注册时一次性所有给定参数,可是这样是不会返回一个uploader实例的;二是注册时不给参数,会返回uploader实例,再对这个uploader实例绑定事件时一步步给出参数。但很明显我这里给定了参数又同时返回了一个uploader实例,只要修改一个源码:打开jquery.plupload.queue.js源码找到定义pluploadQueue这块,将if (settings) 内的返回return this,改为return uploaders[$(this[0]).attr('id')]。这样,点击暂停按钮时,当前上传会暂停,点击开始按钮时,又继续。


三:Controller映射

/**Plupload文件上传处理方法*/ @RequestMapping(value="/pluploadUpload") public void upload(Plupload plupload,HttpServletRequest request,HttpServletResponse response) { String FileDir = "pluploadDir";//文件保存的文件夹 plupload.setRequest(request);//手动传入Plupload对象HttpServletRequest属性 int userId = ((User)request.getSession().getAttribute("user")).getUserId(); //文件存储绝对路径,会是一个文件夹,项目相应Servlet容器下的"pluploadDir"文件夹,还会以用户惟一id做划分 File dir = new File(request.getSession().getServletContext().getRealPath("/") + FileDir+"/"+userId); if(!dir.exists()){ dir.mkdirs();//可建立多级目录,而mkdir()只能建立一级目录 } //开始上传文件 PluploadService.upload(plupload, dir); }

 

在这里,我规定不一样用户上传的资料会根据惟一id分开不一样的文件夹,基于注释代码很容易看懂,你或许会困惑于Plupload与PluploadService,下面就会给出。


四:Plupload类

package web.plupload; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; /** * Plupload实体类固定格式,属性名不可修改 * 由于MultipartFile要用到Spring web的依赖,而该依赖在web模块中才引入,因此不把该实体类放在entity模块 */ public class Plupload { /**文件原名*/ private String name; /**用户上传资料被分解总块数*/ private int chunks = -1; /**当前块数(从0开始计数)*/ private int chunk = -1; /**HttpServletRequest对象,不会自动赋值,须要手动传入*/ private HttpServletRequest request; /**保存文件上传信息,不会自动赋值,须要手动传入*/ private MultipartFile multipartFile; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getChunks() { return chunks; } public void setChunks(int chunks) { this.chunks = chunks; } public int getChunk() { return chunk; } public void setChunk(int chunk) { this.chunk = chunk; } public HttpServletRequest getRequest() { return request; } public void setRequest(HttpServletRequest request) { this.request = request; } public MultipartFile getMultipartFile() { return multipartFile; } public void setMultipartFile(MultipartFile multipartFile) { this.multipartFile = multipartFile; } }
  1. 再次提醒类名与属性名不可随意改变。经过规定好的正确的属性名,客户端经过Http发送块文件至服务端Controller,获得的plupload对象才能传入正确的文件信息。
  2. 关于属性的说明代码注释已经说得很清楚了,能够自行研究学习。

五:PluploadService类

package web.plupload; import org.springframework.util.MultiValueMap; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartHttpServletRequest; import java.io.*; import java.util.Iterator; import java.util.List; /** * Plupload Service模块,同Plupload实体类同样,由于要用到Spring web相关依赖,因此不将其放在Service模块 */ public class PluploadService { public static void upload(Plupload plupload,File pluploadDir){ String fileName = ""+System.currentTimeMillis()+plupload.getName();//在服务器内生成惟一文件名 upload(plupload,pluploadDir,fileName); } private static void upload(Plupload plupload,File pluploadDir,String fileName){ int chunks = plupload.getChunks();//用户上传文件被分隔的总块数 int nowChunk = plupload.getChunk();//当前块,从0开始 //这里Request请求类型的强制转换可能出错,配置文件中向SpringIOC容器引入multipartResolver对象便可。 MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest)plupload.getRequest(); //调试发现map中只有一个键值对 MultiValueMap<String,MultipartFile> map = multipartHttpServletRequest.getMultiFileMap(); if(map!=null){ try{ Iterator<String> iterator = map.keySet().iterator(); while(iterator.hasNext()){ String key = iterator.next(); List<MultipartFile> multipartFileList = map.get(key); for(MultipartFile multipartFile:multipartFileList){//循环只进行一次 plupload.setMultipartFile(multipartFile);//手动向Plupload对象传入MultipartFile属性值 File targetFile = new File(pluploadDir+"/"+fileName);//新建目标文件,只有被流写入时才会真正存在 if(chunks>1){//用户上传资料总块数大于1,要进行合并 File tempFile = new File(pluploadDir.getPath()+"/"+multipartFile.getName()); //第一块直接从头写入,不用从末端写入 savePluploadFile(multipartFile.getInputStream(),tempFile,nowChunk==0?false:true); if(chunks-nowChunk==1){//所有块已经上传完毕,此时targetFile由于有被流写入而存在,要改文件名字 tempFile.renameTo(targetFile); } } else{ //只有一块,就直接拷贝文件内容 multipartFile.transferTo(targetFile); } } } } catch (IOException e){ e.printStackTrace(); } } } private static void savePluploadFile(InputStream inputStream,File tempFile,boolean flag){ OutputStream outputStream = null; try { if(flag==false){ //从头写入 outputStream = new BufferedOutputStream(new FileOutputStream(tempFile)); } else{ //从末端写入 outputStream = new BufferedOutputStream(new FileOutputStream(tempFile,true)); } byte[] bytes = new byte[1024]; int len = 0; while ((len = (inputStream.read(bytes)))>0){ outputStream.write(bytes,0,len); } } catch (FileNotFoundException e){ e.printStackTrace(); } catch (IOException e){ e.printStackTrace(); } finally { try{ outputStream.close(); inputStream.close(); } catch (IOException e){ e.printStackTrace(); } } } }

 

1.PluploadService这个类名是我本身起的,还能够吧~

2.在Controller的最后一行PluploadService.upload(plupload, dir);中将客户端提交至服务器生成的plupload对象与规定保存的文件夹目录,以参数的形式传入PluploadService的upload方法中。

3.在upload(Plupload plupload,File pluploadDir)方法中,为文件生成一个惟一的文件名以便存储在服务器中。

4.chunks是用户一次性选中要上传的文件中当前文件被分隔后的总块数;nowChunk是此次上传中块的编号,从0开始,为何用”此次“呢?前面提到过Plupload就是依次地将块从客户端提交至服务器,所以在文件上传中,会有不少次Http请求,而同一个文件的chunks是不变的,nowChunk会一次次增长。

5.将HttpServletRequest强制转换为MultipartHttpServletRequest时可能会出错,但这个错误能够避免,只需在SpringIOC容器中注入一个名为multipartResolver的对象

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- set the max upload size100MB --> <property name="maxUploadSize"> <value>104857600</value> </property> <property name="maxInMemorySize"> <value>4096</value> </property> <property name="defaultEncoding" value="UTF-8"></property> </bean>

 

6.经过MultipartHttpServletRequest拿到MultiValueMap(通过调试发现这个map只有一对键值对),其Value类型为MultipartFile,这个MultipartFile其实就是当前的块,也难怪为何map中只有一个键值对了。

7.plupload.setMultipartFile(multipartFile);手动为plupload对象传入MultipartFile属性值

8.若是总块数chunks大于1,那就要考虑将上传过来的一个个小块合成一个文件,不然那就直接拷贝块文件到目标文件multipartFile.transferTo(targetFile);

9.在chunks大于1时,首先要新建一个临时文件tempFile,用于不断不断将一个个小块写入这个tempFile,等写完后(chunks-nowChunk==1),就将其重命名(tempFile.renameTo(targetFile);)。

10.savePluploadFile(multipartFile.getInputStream(),tempFile,nowChunk==0?false:true);方法用于合并一个个小块文件,若是是第一块的话,就从头开始写入(new FileOutputStream(tempFile)),不然所有从末端写入(new FileOutputStream(tempFile,true))。


写到这里,基于Plupload实现断点续传的代码已经所有给出了,你们要本身整合至项目中,这里没有给出完整的demo,嗯仍是那句话,授之于鱼不如授之以渔。


上传效果图(登陆了两个用户):

  1. 选定将要上传的文件:

这里写图片描述

2.开始上传,有进度条显示:

这里写图片描述

3.暂停上传:

这里写图片描述

4.暂停后继续上传:

这里写图片描述

5.上传完毕:

这里写图片描述

6.目标文件夹下有相应的文件:

这里写图片描述

这里写图片描述

7.上传过程当中的网络请求,体现断点续传:

这里写图片描述


若有问题或补充,请你们在评论中尽管提,共同交流^~^。

相关文章
相关标签/搜索