SSM框架+Plupload实现分块上传大文件示例

关于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初始化组件

<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映射

  @Autowired
  private PluploadService pluploadService;

  /**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;
  }
}

再次提醒类名与属性名不可随意改变。通过规定好的正确的属性名,客户端通过Http发送块文件至服务端Controller,得到的plupload对象才能传入正确的文件信息。

关于属性的说明代码注释已经说得很清楚了,可以自行研究学习。

五:PluploadService类

package web.plupload;

import entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import java.io.*;
import java.sql.Timestamp;
import java.util.Iterator;
import java.util.List;

import service.youandmeService;

/**
 * Plupload Service模块,同Plupload实体类一样,因为要用到Spring web相关依赖,所以不将其放在Service模块
 */
@Component  //将写好的类注入SpringIOC容器中让Controller自动装载
public class PluploadService {

  @Autowired
  private youandmeService youandmeService;

  public void upload(Plupload plupload,File pluploadDir){
    String fileName = ""+System.currentTimeMillis()+plupload.getName();//在服务器内生成唯一文件名
    upload(plupload, pluploadDir, fileName);
  }

  private 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);

                //每当文件上传完毕,将上传信息插入数据库
                Timestamp now = new Timestamp(System.currentTimeMillis());
                youandmeService.uploadInfo(fileName,((User)(plupload.getRequest().getSession().getAttribute("user"))).getUsername(),now);
              }
            }
            else{
              //只有一块,就直接拷贝文件内容
              multipartFile.transferTo(targetFile);

              //每当文件上传完毕,将上传信息插入数据库
              Timestamp now = new Timestamp(System.currentTimeMillis());
              youandmeService.uploadInfo(fileName, ((User) (plupload.getRequest().getSession().getAttribute("user"))).getUsername(), now);
            }
          }
        }
      }
      catch (IOException e){
        e.printStackTrace();
      }
    }
  }
  private 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.上传过程中的网络请求,体现分块上传:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 基于SSM框架+Javamail发送邮件的代码实例

    本篇介绍基于SSM框架(Spring4.0+SpringMVC+Mybatis)组合的Javamail应用,邮箱的话基于腾讯的QQ邮箱,其实也是Foxmail邮箱 先要了解一下SMTP协议和SSL加密 SMTP:称为简单邮件传输协议(Simple Mail Transfer Protocal),目标是向用户提供高效.可靠的邮件传输.SMTP是一种请求响应的协议,也就是客户机向远程服务器发送请求,服务器响应,监听端口是25,所以其工作模式有两种:发送SMTP,接收SMTP. SSL加密:用来保障浏

  • 基于SSM框架之个人相册示例代码

    学习了一阵子的SSM框架,一直在各种博客,简书,慕课网学习,最后终于自己撸出来一个简单的个人相册. 项目的演示效果: 开发的工具及环境: IntelliJ IDEA: 2016 Maven :3.0x Hbuilder(前端部分,可以用记事本代替2333) Java 8 项目流程(dao->service->web): 1.添加所有依赖: <dependency> <groupId>junit</groupId> <artifactId>juni

  • ssm框架上传图片保存到本地和数据库示例

    本文介绍了ssm框架上传图片保存到本地和数据库示例,主要使用了Spring+SpringMVC+MyBatis框架,实现了ssm框架上传图片的实例,具体如下: 1.前台部分 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head&g

  • Maven+SSM框架实现简单的增删改查

    Spring介绍: spring 使用基本的 JavaBean 来完成以前只可能由 EJB 完成的事情.然而, Spring的用途不仅限于服务器端的开发.从简单性.可测试性和松耦合的角度而言,任何Java 应用都可以从 Spring 中受益. 简单来说, Spring 是一个轻量级的控制反转(IoC )和面向切面( AOP )的容器框架. SpringMVC介绍 Spring MVC 属于 SpringFrameWork 的后续产品,已经融合在Spring Web Flow 里面. Spring

  • SSM框架+Plupload实现分块上传大文件示例

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

  • asp.net 上传大文件解决方案

    这次在项目中,用到了大文件上传,要上传的文件有100多m,于是研究现在国内使用的大文件上传的组件发现用的比较多的有两个控件AspnetUpload 2.0和Lion.Web.UpLoadModule,另外还有思归在它的博客堂中所说的办法 http://blog.joycode.com/saucer/archive/2004/03/16/16225.aspx   两个控件的方法是:利用隐含的HttpWorkerRequest,用它的GetPreloadedEntityBody 和 ReadEnti

  • HTTP协议下用Web Service上传大文件的解决方案

    用HTTP协议上传大文件也许是个不好办的问题.主要是它的不连续性,使得上传文件感觉很"危险".特别是很大的文件(几百MB甚至是上G的文件),心里总觉得不踏实,一不小心就会出现问题,而一但出现问题就无法继续上传,这是很郁闷的. 后来在一些网站上找到一些上传文件的组件,但都是要用到一些COM组件.至于后来的ASP.net下上传大文件的解决方案,我也做过一个组件,后来发现根本就不用自己写什么组件,利用ASP.net自己的上传方法也可以解决大文件上传,真是郁闷的要死了.... 回想之后,决定用

  • Java中使用WebUploader插件上传大文件单文件和多文件的方法小结

    一.使用webuploader插件的原因说明 被现在做的项目坑了. 先说一下我的项目架构spring+struts2+mybatis+MySQL 然后呢.之前说好的按照2G上传就可以了,于是乎,用了ajaxFileUpload插件,因为之前用图片上传也是用这个,所以上传附件的时候就直接拿来用了 各种码代码,测试也测过了,2G文件上传没问题,坑来了,项目上线后,客户又要求上传4G文件,甚至还有20G以上的..纳尼,你不早说哦... 在IE11下用ajaxFileUpload.js插件上传超过4G的

  • ASP.NET解决上传大文件问题的方法

    上传文件的控件为:FileUpload Asp.Net对上传文件大小有限制.默认情况下用户只能上传4MB大小的文件,这会给用户带来不便.所以如果要上传40MB大小的文件.只能修改配置文件 关键代码如下 复制代码 代码如下: protected void btnSend_Click(object sender, EventArgs e) { try { //上传文件的思路: //获取上传文件的名称,此时为一个全路径的地址 string upFileName = fulFileName.FileNa

  • FireFox浏览器使用Javascript上传大文件

    本程序是利用3.x的Firefox浏览器可以读取本地文件的特性,实现通过xmlHttPRequest上传大文件功能,并在可以上传过程中动态显示上传进度.略加修改,并与服务器端配合,可以实现断点续传等诸多功能.本例主要是研究FireFox的file-input节点的一些特性,其他客户端应用,如Flash.Sliverlight等,在实现客户端大文件上传时,在数据传输与服务器端存储等方面,与本例的思路基本一致.注意:文件体积似乎有临界点,但这个临界点是多少尚未确认.建议不要用此方法上传超过100M的

  • php上传大文件失败的原因及应对策略

    为什么上传大文件总是失败,但是上传小文件就没有问题.小编也不得其解,网上搜其原因,整理了一篇关于php上传大文件失败的原因和解决办法的文章,分享给大家. 下面分别是各种原因以及解决办法: 第1种情况:文件上传时存放文件的临时目录必须是开启的并且是 PHP 进程所有者用户可写的目录.如果未指定则 PHP 使用系统默认值. php.ini文件中upload_tmp_dir用来说明PHP上传的文件放置的临时目录,要想上传文件,得保证服务器没有关闭临时文件并对该文件夹有写入的权限. 第2种情况:max_

  • Nginx 上传大文件超时解决办法

    Nginx 上传大文件超时解决办法 情况如下:用nginx作代理服务器,上传大文件时(本人测试上传50m的文件),提示上传超时或文件过大. 原因是nginx对上传文件大小有限制,而且默认是1M.另外,若上传文件很大,还要适当调整上传超时时间. 解决方法是在nginx的配置文件下,加上以下配置: client_max_body_size 50m; //文件大小限制,默认1m client_header_timeout 1m; client_body_timeout 1m; proxy_connec

  • win2008 iis7 上传大文件限制的真正解决办法

    iis7 上传大文件限制的真正解决办法 修改IIS_schema.xml这个文件要先获得这个文件的控制权; 进入目录C:\Windows\System32\inetsrv\config\schema,修改文件IIS_schema.xml 权限: 进入IIS_schema.xml文件权限修改,选择"高级" 选择"所有者" 选中 administrators 确定 再进入权限编辑,修改administrators 完全控制. 再去掉IIS_schema.xml的只读属性

  • 原生JS上传大文件显示进度条 php上传文件代码

    JS原生上传大文件显示进度条,php上传文件,供大家参考,具体内容如下 在php.ini修改需要的大小: upload_max_filesize = 8M    post_max_size = 10M    memory_limit = 20M <!DOCTYPE html> <html> <head> <title>原生JS大文件显示进度条</title> <meta charset="UTF-8"> <s

随机推荐