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


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


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

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

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

  3. 在项目中需要引入: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初始化组件

<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.上传过程中的网络请求,体现断点续传:


如有问题或补充,请大家在评论中尽管提,共同交流^~^。

SSM框架+Plupload实现断点续传(Spring+SpringMVC+MyBatis+Plupload)的更多相关文章

  1. SSM框架—详细整合教程(Spring+SpringMVC+MyBatis)

    很久没有新搭建过框架了,今天搭建一遍.以往都是在eclipse中搭建,今天换Idea吧,目前来说Idea用的还是很多的,但是用习惯了eclipse的朋友,可能会不太习惯 ok.....开始: 注意区分 ...

  2. SSM三大框架整合配置(Spring+SpringMVC+MyBatis)

    web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi=" ...

  3. SSM整合+WebUpload使用(spring+springmvc+mybatis+maven)

      SSM框架整合以及webupload的集成与使用 在项目中最近用到了webupload.js,也方方面面遇到了不少问题,比如上传文件前对表单参数校验,当校验失败不予提交,及在文件上传成功后,选择同 ...

  4. [JSP]Maven+SSM框架(Spring+SpringMVC+MyBatis) - Hello World

    来源:http://blog.csdn.net/zhshulin/article/details/37956105?utm_source=tuicool&utm_medium=referral ...

  5. SSM框架——详细整合教程(Spring+SpringMVC+MyBatis)【转载】

    最近在学习Spring+SpringMVC+MyBatis的整合.以下是参考网上的资料自己实践操作的详细步骤. 1.基本概念   1.1.Spring Spring是一个开源框架,Spring是于20 ...

  6. SSM框架——Spring+SpringMVC+Mybatis的搭建教程

    一:概述 SSM框架在项目开发中经常使用到,相比于SSH框架,它在仅几年的开发中运用的更加广泛. Spring作为一个轻量级的框架,有很多的拓展功能,最主要的我们一般项目使用的就是IOC和AOP. S ...

  7. Maven+SSM框架搭建【spring+springmvc+mybatis】

    本案例用到:ssm[spring+springmvc+mybatis]框架 数据库:mysql (推荐使用mysql 或者 sqlserver  .oracle太大,一般大型项目才会用到) 开发工具: ...

  8. SSM(Spring + Springmvc + Mybatis)框架面试题

    JAVA SSM框架基础面试题https://blog.csdn.net/qq_39031310/article/details/83050192 SSM(Spring + Springmvc + M ...

  9. SSM框架整合(IntelliJ IDEA + maven + Spring + SpringMVC + MyBatis)

    本篇文章主要内容是介绍如何使用IntelliJ IDEA创建Spring + SpringMVC + MyBatis项目,下面会给出项目搭建的详细步骤以及相关的配置文件. 1. 创建maven项目   ...

随机推荐

  1. [Javascript] The Array forEach method

    Most JavaScript developers are familiar with the for loop. One of the most common uses of the for lo ...

  2. android109 结构体,联合体,枚举,自定义

    #include <stdio.h> #include <stdlib.h> void study(){ printf("吃饭睡觉打李志\n"); } // ...

  3. [Form Builder]NAME_IN()与COPY()

    NAME_IN和COPY实际是间接引用,类似指针传递,而不是值传递... IF :VAR1 IS NULL ...  direct referenceIF NAME_IN ( :VAR1 ) IS N ...

  4. YII中的session和cookie

    session的使用 存储数据 Yii::app()->session["名"] = 值; 取数据 $变量 = Yii::app()->session["名& ...

  5. 关于dialog引起的 java.lang.IllegalArgumentException: View=com.android.internal.policy.impl.PhoneWindow$DecorView not attached to window manager 错误的分析

    在跑Monkey测试的时候出现了一个比较特别的问题,先来看看Log: // CRASH: com.meizu.media.painter (pid 12491) // Short Msg: java. ...

  6. Java输出日历

    源码链接:http://pan.baidu.com/s/1o6xeybK

  7. OC加强-day03

    #program mark - 0_18 分类的使用注意 [掌握] 1.分类的作用 作用:讲一个类分为多个模块,将相似功能的方法写在同一个模块中,方便我们后面代码的维护 "强调 1.分类中只 ...

  8. c# web页面乱码

    1.在web.config中加入:<globalization requestEncoding="GB2312" responseEncoding="GB2312& ...

  9. 执行mysql脚本

    方法1: mysql -uroot -p123456 -Ddatabasename < sql文件路径 方法2: source 路径全名 \. 路径全名

  10. redis基本数据类型【2】-Hash类型

    一.概述 1.散列是一种典型的字典结构,filed和value的映射,但value只能存储字符串,不支持其他类型 2.一个散列类型最多包含 2^32 -1个字段 3.散列适合存储对象:使用对象和ID构 ...