无组件断点续传.gif
1. 组件简介

webuploader:是一个以HTML5为主, Flash为辅的文件上传组件,采用大文件分片/并发上传的方式,极大地提高了文件上传的效率,同时兼容多种浏览器版本;

2. 项目背景简介

本篇文章的背景,是在上一篇文章(《无组件实现大文件分片上传,断点续传》)的项目背景下进行的一次尝试,所以本篇文章还是基于上一篇文章的背景,但是不会介绍视频基本信息(视频标题、简介、播出时间等)的操作,主要介绍文件的上传。因为项目的特殊需求,这种使用插进的方式最终没有被采用,因为一些控件无法做到定制化。
上一篇文章(《无组件实现大文件分片上传,断点续传》)中介绍的文件上传方式,在前端主要采用纯JavaScript来进行文件切分、验证,后台主要采用了NIO的方式进行分片的追加。而在这篇文章中,将介绍前端采用webuploader,后台采用临时目录+传统I/O方式进行分片合并的方式。

3. 技术实现
3.1 组件引入

在webuploader官网下载必要的文件,放入项目中。在页面中进行引入;

0_组件引入.png
<!-- webuploader文件上传 -->
<script src="static/webuploader/webuploader.nolog.min.js"></script>
<link href="static/webuploader/webuploader.css" rel="stylesheet" type="text/css" />复制代码
3.2 前端页面实现

在前端页面中,可以不用关心css样式,但需要注意标签的id/nama属性,这些将在后面的JavaScript中使用到。

1-页面实现.png
<!-- Main content --><section class="content"><div class="container" style="margin-top: 20px"><div class="alert alert-info">可以一次上传多个大文件</div></div><div class="container" style="margin-top: 50px"><div id="uploader" class="container"><div class="container"><div id="fileList" class="uploader-list"></div><!--存放文件的容器--></div><div class="btns container"><div id="picker" class="webuploader-container"style="float: left; margin-right: 10px"><div>选择文件 <input type="file" name="file"class="webuploader-element-invisible" multiple="multiple"></div></div><div id="UploadBtn" class="webuploader-pick"style="float: left; margin-right: 10px">开始上传</div><div id="StopBtn" class="webuploader-pick"style="float: left; margin-right: 10px" status="suspend">暂停上传</div></div></div></div></section>复制代码
3.3 使用组件实现文件的上传、切分、发送

在这部分,将使用组件完成文件上传、MD5验证、删除、切片、上传进度条显示、暂停、继续上传及上传成功/失败时候的回调。

无组件断点续传.gif
<script type="text/javascript">$(function () {$list = $('#fileList');var flie_count = 0;var uploader = WebUploader.create({//设置选完文件后是否自动上传auto: false,//swf文件路径swf: 'static/webuploader/Uploader.swf',// 文件接收服务端。server: 'micro/BigFileUp',// 选择文件的按钮。可选。// 内部根据当前运行是创建,可能是input元素,也可能是flash.pick: '#picker',chunked: true, //开启分块上传chunkSize: 10 * 1024 * 1024,chunkRetry: 3,//网络问题上传失败后重试次数threads: 1, //上传并发数//fileNumLimit :1,fileSizeLimit: 2000 * 1024 * 1024,//最大2GBfileSingleSizeLimit: 2000 * 1024 * 1024,resize: false//不压缩//选择文件类型//accept: {//    title: 'Video',//    extensions: 'mp4,avi',//    mimeTypes: 'video/*'//}});// 当有文件被添加进队列的时候uploader.on('fileQueued', function (file) {$list.append('<div id="' + file.id + '" class="item">' +'<h4 class="info">' + file.name + '<button type="button" fileId="' + file.id + '" class="btn btn-danger btn-delete"><span class="glyphicon glyphicon-trash"></span></button></h4>' +'<p class="state">正在计算文件MD5...请等待计算完毕后再点击上传!</p><input type="text" id="s_WU_FILE_'+flie_count+'" />' +'</div>');console.info("id=file_"+flie_count);flie_count++;//删除要上传的文件//每次添加文件都给btn-delete绑定删除方法$(".btn-delete").click(function () {//console.log($(this).attr("fileId"));//拿到文件iduploader.removeFile(uploader.getFile($(this).attr("fileId"), true));$(this).parent().parent().fadeOut();//视觉上消失了$(this).parent().parent().remove();//DOM上删除了});//uploader.options.formData.guid = WebUploader.guid();//每个文件都附带一个guid,以在服务端确定哪些文件块本来是一个//console.info("guid= "+WebUploader.guid());//md5计算uploader.md5File(file).progress(function(percentage) {console.log('Percentage:', percentage);})// 完成.then(function (fileMd5) { // 完成var end = +new Date();console.log("before-send-file  preupload: file.size="+file.size+" file.md5="+fileMd5);file.wholeMd5 = fileMd5;//获取到了md5//uploader.options.formData.md5value = file.wholeMd5;//每个文件都附带一个md5,便于实现秒传$('#' + file.id).find('p.state').text('MD5计算完毕,可以点击上传了');console.info("MD5="+fileMd5);});});// 文件上传过程中创建进度条实时显示。uploader.on('uploadProgress', function (file, percentage) {var $li = $('#' + file.id),$percent = $li.find('.progress .progress-bar');// 避免重复创建if (!$percent.length) {$percent = $('<div class="progress progress-striped active">' +'<div class="progress-bar" role="progressbar" style="width: 0%">' +'</div>' +'</div>').appendTo($li).find('.progress-bar');}$li.find('p.state').text('上传中');$percent.css('width', percentage * 100 + '%');});//发送前填充数据uploader.on( 'uploadBeforeSend', function( block, data ) {// block为分块数据。// file为分块对应的file对象。var file = block.file;var fileMd5 = file.wholeMd5;// 修改data可以控制发送哪些携带数据。console.info("fileName= "+file.name+" fileMd5= "+fileMd5+" fileId= "+file.id);console.info("input file= "+ flie_count);// 将存在file对象中的md5数据携带发送过去。data.md5value = fileMd5;//md5data.fileName_ = $("#s_"+file.id).val();console.log("fileName_: "+data.fileName_);// 删除其他数据// delete data.key;if(block.chunks>1){ //文件大于chunksize 分片上传data.isChunked = true;console.info("data.isChunked= "+data.isChunked);}else{data.isChunked = false;console.info("data.isChunked="+data.isChunked);}});uploader.on('uploadSuccess', function (file) {$('#' + file.id).find('p.state').text('已上传');$('#' + file.id).find(".progress").find(".progress-bar").attr("class", "progress-bar progress-bar-success");$('#' + file.id).find(".info").find('.btn').fadeOut('slow');//上传完后删除"删除"按钮$('#StopBtn').fadeOut('slow');});uploader.on('uploadError', function (file) {$('#' + file.id).find('p.state').text('上传出错');//上传出错后进度条变红$('#' + file.id).find(".progress").find(".progress-bar").attr("class", "progress-bar progress-bar-danger");//添加重试按钮//为了防止重复添加重试按钮,做一个判断//var retrybutton = $('#' + file.id).find(".btn-retry");//$('#' + file.id)if ($('#' + file.id).find(".btn-retry").length < 1) {var btn = $('<button type="button" fileid="' + file.id + '" class="btn btn-success btn-retry"><span class="glyphicon glyphicon-refresh"></span></button>');$('#' + file.id).find(".info").append(btn);//.find(".btn-danger")}$(".btn-retry").click(function () {//console.log($(this).attr("fileId"));//拿到文件iduploader.retry(uploader.getFile($(this).attr("fileId")));});});uploader.on('uploadComplete', function (file) {//上传完成后回调//$('#' + file.id).find('.progress').fadeOut();//上传完删除进度条//$('#' + file.id + 'btn').fadeOut('slow')//上传完后删除"删除"按钮});uploader.on('uploadFinished', function () {//上传完后的回调方法//alert("所有文件上传完毕");//提交表单});$("#UploadBtn").click(function () {uploader.upload();//上传});$("#StopBtn").click(function () {console.log($('#StopBtn').attr("status"));var status = $('#StopBtn').attr("status");if (status == "suspend") {console.log("当前按钮是暂停,即将变为继续");$("#StopBtn").html("继续上传");$("#StopBtn").attr("status", "continuous");console.log("当前所有文件==="+uploader.getFiles());console.log("=============暂停上传==============");uploader.stop(true);console.log("=============所有当前暂停的文件=============");console.log(uploader.getFiles("interrupt"));} else {console.log("当前按钮是继续,即将变为暂停");$("#StopBtn").html("暂停上传");$("#StopBtn").attr("status", "suspend");console.log("===============所有当前暂停的文件==============");console.log(uploader.getFiles("interrupt"));uploader.upload(uploader.getFiles("interrupt"));}});uploader.on('uploadAccept', function (file, response) {if (response._raw === '{"error":true}') {return false;}});});</script>复制代码

以上为前端代码的实现

3.4 后台分片接收

在后台分片接收部分,主要是判断文件是否有分片,如果没有,则直接存放到目的目录;如果存在分片,则创建临时目录,存放分片信息;之后判断当前分片所属的文件的所有分片是否已经传输完毕,如果当前分片数==所属文件总分片数,则开始合并文件并转移完整文件到目的目录,并且删除临时目录;
如下图,是上传文件时所创建的临时目录及目录中的临时文件;

2-临时目录.png
3-临时文件.png

Controller实现

/*** * @Description: *             接受文件分片,合并分片* @param guid   *             可省略;每个文件有自己唯一的guid,后续测试中发现,每个分片也有自己的guid,所以不能使用guid来确定分片属于哪个文件。* @param md5value*             文件的MD5值* @param chunks*             当前所传文件的分片总数* @param chunk*             当前所传文件的当前分片数* @param id*             文件ID,如WU_FILE_1,后面数字代表当前传的是第几个文件,后续使用此ID来创建临时目录,将属于该文件ID的所有分片全部放在同一个文件夹中* @param name*             文件名称,如07-中文分词器和业务域的配置.avi* @param type*             文件类型,可选,在这里没有用到* @param lastModifiedDate 文件修改日期,可选,在这里没有用到* @param size  当前所传分片大小,可选,没有用到* @param file  当前所传分片* @return* @author: xiangdong.she* @date: Aug 20, 2017 12:37:56 PM */@ResponseBody@RequestMapping(value = "/BigFileUp")public String fileUpload(String guid, String md5value, String chunks, String chunk, String id, String name,String type, String lastModifiedDate, int size, MultipartFile file) {String fileName;JSONObject result=new JSONObject();try {int index;String uploadFolderPath = FileUtil.getRealPath(request);String mergePath = uploadFolderPath + "\\fileDate\\" + id + "\\";String ext = name.substring(name.lastIndexOf("."));// 判断文件是否分块if (chunks != null && chunk != null) {index = Integer.parseInt(chunk);fileName = String.valueOf(index) + ext;// 将文件分块保存到临时文件夹里,便于之后的合并文件FileUtil.saveFile(mergePath, fileName, file, request);// 验证所有分块是否上传成功,成功的话进行合并FileUtil.Uploaded(md5value, guid, chunk, chunks, mergePath, fileName, ext, request);} else {SimpleDateFormat year = new SimpleDateFormat("yyyy");SimpleDateFormat m = new SimpleDateFormat("MM");SimpleDateFormat d = new SimpleDateFormat("dd");Date date = new Date();String destPath = uploadFolderPath + "\\fileDate\\" + "video" + "\\" + year.format(date) + "\\"+ m.format(date) + "\\" + d.format(date) + "\\";// 文件路径String newName = System.currentTimeMillis() + ext;// 文件新名称// fileName = guid + ext;// 上传文件没有分块的话就直接保存目标目录FileUtil.saveFile(destPath, newName, file, request);}} catch (Exception ex) {ex.printStackTrace();result.put("code", 0);result.put("msg", "上传失败");result.put("data", null);return result.toString();}result.put("code", 1);result.put("msg", "上传成功");return result.toString();}复制代码
3.5文件I/O操作实现

此部分代码较多,已将FileUtil上传至Batatas项目下的Util目录(喜欢Batatas这个项目的小伙伴,别忘了点个star哟,或者也非常欢迎加入我们),在这部分实现中,主要用到了一下几个方法:

  • saveFile()//保存分片至临时目录,或者保存未拆分文件到目标目录;
  • mergeFile()//合并临时目录中的临时文件,并将合并后的文件转移至目标目录;
  • saveStreamToFile()//使用I/O流合并分片文件
  • getSavePath()//获取文件保存的路径,如果没有该目录,则创建,可用于临时目录或目标存放目录的创建;
  • isAllUploaded()//在fileUtil中,使用一个全局的uploadInfoList去存放,已经上传的分片信息;在合并分片之前,首先回去这个List中检查属于该文件的所有分片信息是否已经存在,如果不存在,则不合并;如果已全部存在,则将这些信息从list中删除,并开始合并分片;
4. 总结

本篇文章主要介绍了使用百度Webuploader组件进行大文件的分片上传、断点续传,以及服务器端分片合并与转移。

使用webuploader组件实现大文件分片上传,断点续传相关推荐

  1. minio实现大文件分片上传+断点续传+预览

    minio实现大文件分片上传+断点续传+预览 只提供后端java代码 思路: 前端分片 校验文件md5是否已经存在 --不存在创建临时桶存分片 校验分块是否已经上传 分块上传 合并分块 校验合成后md ...

  2. vue实现大文件分片上传断点续传并展示上传进度条

    最近有一个上传视频到服务器的功能,然后发现视频太大了,比如1个G的视频文件基本都是上传失败的,我之前都是上传阿里云的,所以面对大文件上传服务器就做了分片上传和断点续传. 首先解释什么是分片上传:比如一 ...

  3. Vue项目中遇到了大文件分片上传的问题

    Vue项目中遇到了大文件分片上传的问题,之前用过webuploader,索性就把Vue2.0与webuploader结合起来使用,封装了一个vue的上传组件,使用起来也比较舒爽. 上传就上传吧,为什么 ...

  4. 大文件分片上传,断点续传,秒传 实现

    前段时间做视频上传业务,通过网页上传视频到服务器. 视频大小 小则几十M,大则 1G+,以一般的HTTP请求发送数据的方式的话,会遇到的问题:1,文件过大,超出服务端的请求大小限制:2,请求时间过长, ...

  5. jquery 分片上传php,php 大文件分片上传

    前端部分 上传 //上传控件 uploadBig('upload','zip,rar,7z,tar',{ id: '', type: 'upload_file', } ,(res)=>{ //t ...

  6. 大文件分片上传前后端实现

    最近在做公司的视频业务,涉及到大视频的上传. 之前的图片.Excel等上传做的很简单,直接表单提交后端用MultipartFile接收保存到磁盘就行了. 但是针对大文件的上传,需要做额外的处理,否则可 ...

  7. java实现大文件分片上传

    java实现大文件分片上传 在项目中用到了大文件上传功能,最初从网上参考了一些代码来实现,但是最终的上传效果不是很好,速度比较慢. 之前的上传思路是: 前端利用webUploader分片大文件 后端接 ...

  8. React大文件分片上传方案

    最近做了大文件(文件夹)分片上传的需求,记录一下. 原理: 前端进行大文件分片上传的方案几乎都是利用Blob.prototype.slice方法对文件进行分片,用数组将每一个分片存起来,最后将分片发给 ...

  9. 大文件分片上传前端框架_基于Node.js的大文件分片上传

    基于Node.js的大文件分片上传 我们在做文件上传的时候,如果文件过大,可能会导致请求超时的情况.所以,在遇到需要对大文件进行上传的时候,就需要对文件进行分片上传的操作.同时如果文件过大,在网络不佳 ...

最新文章

  1. java堆内存与栈内存区别
  2. Ubuntu18.04安装JDK1.8和maven3
  3. 嵌入式笔录(4)-LC并联电路选频和AM解调
  4. JAVA设计模式之装饰模式
  5. ansible 下lineinfile详细使用
  6. 程序员越老越优秀吗?
  7. 计算机应用技术专业考试试题,全国专业技术人员计算机应用能力考试模拟试题笔试题.docx...
  8. idea中Tomcat中文乱码问题
  9. Luogu 3332 [ZJOI2013]K大数查询
  10. google和baidu搜索命令
  11. Python爬取招聘网站岗位信息
  12. SCOI 2016 bzoj 4567~4572 题解
  13. WTP协议简要翻译一(dlmu2001)
  14. kali 安装 netspeed 扩展
  15. 中国大学的现实是:大学层次越低,上课越多,学生读书越少
  16. SpringSecurity 简介
  17. 平行因子-三维荧光-PARAFAC数据前处理
  18. docker下载镜像新建容器打包容器打包tar加载tar
  19. html圆角边框背景颜色,CSS之圆角边框渐变的实现
  20. tomcat启动(一)

热门文章

  1. lucene bug的报告经历
  2. 鼠标缩略图遥感图像显示时的连动效果——Qt实现
  3. 安装/卸载.deb文件
  4. 鲜活的数据 : 数据可视化指南
  5. js控制input只能输入数字和小数点后两位,输入其他自动清除方法。
  6. fabricjs上手指南
  7. dirty_ratio与dirty_background_ratio参数区别--系统优化必选
  8. Linux的DNS高速缓存
  9. egret dragonbones部件替换产生位移的解决方案
  10. Oracle DataGuard 之--Physical DG转换Logical DG