关于Plupload的介绍,相信它的官网http://www.plupload.com/已经给得很详细了。Plupload的上传原理简单点说,就是将用户选中的文件(可多个)分隔成一个个小块,依次向服务器上传,这是它能驾驭上传大文件的原因之一,而且在这个过程可以暂停上传,暂停后再继续上传。最重要的是,从头到尾没有一点点UI阻塞,保证了用户体验。下面会开始讲Plupload的实现流程,分析原理,并在最后给出效果图。
在此之前先说说我的项目,做的j2ee项目运用到Spring+SpringMVC+MyBatis的框架集合,是关于一个社交平台的网站,类似于facebook,twitter,微博等,起了一个名字叫YouAndMe。我大胆地构想了这个项目应该有一个用户资料共享的平台,或是一部好看的电影,或是一套电视剧,或是居家必备的食谱,也有可能是好看的风景图,各式各样。用户可以搜索想要的资料并下载。因此首先要解决的就是各式各样(大)文件的上传。
一:下载Plupload插件并引入相应文件
-
值得一提的是这个插件只是前端的,后台怎么获取怎么将一个个小块合并等代码是要自己写的。
-
下载地址:http://www.plupload.com/download,我下的是Plupload 2.1.9 GPLv2版本的,里面有要用到的css以及js。
-
在项目中需要引入:jQuery.plupload.queue.css,jquery-2.0.0.min.js,plupload.full.min.js,jquery.plupload.queue.js,zh_CN.js这些文件。
二:前端准备
1.首先在html中写入如下代码:
- <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的按钮是我自己加的,目的是为了实现暂停上传与暂停过后的继续上传。
2.页面加载后通过js初始化组件
- <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的按钮是我自己加的,目的是为了实现暂停上传与暂停过后的继续上传。
-
- 2.页面加载后通过js初始化组件
-
- <script type="text/javascript">
- $(function() {
-
- var uploader = $("#uploader").pluploadQueue({
-
- runtimes: 'html5,flash,silverlight,html4',
- url: "../pluploadUpload",
-
-
- max_file_size: '10000mb',
-
- chunk_size: '1mb',
-
-
- resize: {
- width: 200,
- height: 200,
- quality: 90,
- crop: true
- },
-
-
- filters: [
- {title: "Image files", extensions: "jpg,gif,png"},
- {title: "Vedio files", extensions: "mp4,mkv"},
- {title: "Zip files", extensions: "zip,avi"}
- ],
-
-
- rename: true,
-
-
- sortable: true,
-
-
- dragdrop: true,
-
-
- views: {
- list: true,
- thumbs: true,
- active: 'thumbs'
- },
-
-
- flash_swf_url: 'js/Moxie.swf',
-
-
- 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映射
-
- @RequestMapping(value="/pluploadUpload")
- public void upload(Plupload plupload,HttpServletRequest request,HttpServletResponse response) {
-
- String FileDir = "pluploadDir";
- plupload.setRequest(request);
-
- int userId = ((User)request.getSession().getAttribute("user")).getUserId();
-
-
- File dir = new File(request.getSession().getServletContext().getRealPath("/") + FileDir+"/"+userId);
- if(!dir.exists()){
- dir.mkdirs();
- }
-
- PluploadService.upload(plupload, dir);
- }
在这里,我规定不同用户上传的资料会根据唯一id分开不同的文件夹,基于注释代码很容易看懂,你或许会困惑于Plupload与PluploadService,下面就会给出。
四:Plupload类
- package web.plupload;
-
- import org.springframework.web.multipart.MultipartFile;
-
- import javax.servlet.http.HttpServletRequest;
-
-
-
-
-
- public class Plupload {
-
- private String name;
-
- private int chunks = -1;
-
- private int chunk = -1;
-
- 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;
- }
- }
- 再次提醒类名与属性名不可随意改变。通过规定好的正确的属性名,客户端通过Http发送块文件至服务端Controller,得到的plupload对象才能传入正确的文件信息。
- 关于属性的说明代码注释已经说得很清楚了,可以自行研究学习。
五: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;
-
-
-
-
- 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();
-
-
- MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest)plupload.getRequest();
-
- MultiValueMap map = multipartHttpServletRequest.getMultiFileMap();
-
- if(map!=null){
- try{
- Iterator iterator = map.keySet().iterator();
- while(iterator.hasNext()){
-
- String key = iterator.next();
- List multipartFileList = map.get(key);
-
- for(MultipartFile multipartFile:multipartFileList){
-
- plupload.setMultipartFile(multipartFile);
- File targetFile = new File(pluploadDir+"/"+fileName);
- if(chunks>1){
-
- File tempFile = new File(pluploadDir.getPath()+"/"+multipartFile.getName());
-
- savePluploadFile(multipartFile.getInputStream(),tempFile,nowChunk==0?false:true);
-
- if(chunks-nowChunk==1){
- 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">
-
- <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)
)。
转载:https://blog.csdn.net/qq_19707521/article/details/52288139