接下来,就要实现layui的uploader分块上传了,在官网上没有提到分块上传,倒是有一个多文件选择后,显示文件列表的例子。
目录
现状分析
对于我们能有啥启发呢,其实主要就是使用了choose事件,然后有个preview方法,可以针对每个文件进行处理(如图片文件预览)
问了下度娘,网上也有layui分块上传的例子,基本原理就是在上述preview方法中,对单个文件进行分块,多次调用上传,即可实现分块上传。
但是网上的例子有个问题就是:只能是单个文件分块上传,多个文件是不支持的。不过在了解清楚他的思路以后,多文件分块上传也是可以做的。
分块上传的基本思路:把一个大文件按照一个标准,对文件流分隔,分别调用上传,最后一片调用完后,返回上传完成,后续不再调用上传。
网上的例子,之所以只能单个文件分块上传,是因为对于上传状态的标识存储到了页面的hidden控件,那么我要实现多文件分块上传,那么我让每个文件都有一个自己的上传状态存储就可以了。
我的做法
在choose方法中,循环files,动态创建tr行,在每行存储一个上传状态的hidden,每次调用完上传后也是通过相应规则去检查返回的上传状态就行了。
这里还有个问题,preview方法在文件比较大的情况下,可能上传无反应,这个度娘也跟我说了,所以就直接遍历files就好了,不过对于已经上传过的文件,需要自己做下处理。
这里的一个改动,又引起了一个新的问题,就是之前调用preview方法的时候,用了一个定时器interval,用于定时调用上传接口,在上传完成后清理掉定时器。之前是单文件分块上传,所以定义一个定时器即可,当我是多文件分块上传的时候,就需要可以动态定义多个定时器了,于是使用了eval方法。就是先根据一个规则,动态定义一个不同名字的定时器,不过脚本是写成字符串,然后再eval执行。
var xx = 'var progressTimer' + f + ' = setInterval(function () {\
uploadxx(data, obj, file, LENGTH, fileId, fileName, index,progressTimer'+ f + ')\
}, 100);';
eval(xx);
一开始uploadxx方法其实是upload,但是发现调用没反应,估计是和layui自己的upload方法冲突了,改下方法名就行了。
功能优化
基本功能具备后,还要考虑实际应用。
怎么说呢,上传文件,我需要显示上传进度,上传完成后,我还需要绑定一个文件列表(业务),于是决定,选择文件后,弹出层显示上传进度相关内容,点击完成关闭弹出层,再绑定业务的文件列表。
上代码了
var uploadInstNew = upload.render({
elem: '#chunkUploadNew' //绑定元素
, elemList: $('#fileList1') //列表元素对象
, url: '<%=AppPath%>/Sys/Handler/FileUploadHandler.ashx?action=Upload' //上传接口
, accept: 'file'
, auto: false
, multiple: true
//, size: 100 * 1024
, choose: function (obj) {
var that = this;
var data = this.data;
var files = obj.pushFile();
var LENGTH = 1024 * 1024; //每片文件大小1mb
var f = 0;
var layer = layui.layer;
layer.open({
type: 1,
title: '文件上传',
content: $('#fileUploadList1'),
area: ['900px', '350px'],
btnAlign: 'c',
maxmin: true,
closeBtn: 0,
moveOut: true,
btn: ['完成'],
yes: function (index, layero) {
layer.close(index);
},
end: function () {
},
});
for (var key in files) {
var file = files[key];
var index = key;
//已经上传过的文件跳过
var tr = that.elemList.find('tr#upload-' + index);
if (tr.length > 0) {
continue;
}
f++;
var fileId = newGuid();
//alert(index);
var totalSize = file.size;
var totalPage = Math.ceil(totalSize / LENGTH);
var tr = $(['<tr id="upload-' + index + '">'
, '<td>' + file.name + '</td>'
, '<td>' + (file.size / 1014).toFixed(1) + 'kb</td>'
, '<td>'
, '<div class="layui-progress" lay-showpercent="true" lay-filter="progress-demo-' + index + '"><div class="layui-progress-bar" lay-percent=""></div></div>',
, '<input type="hidden" id="totalPage" value="0" />'
, '<input type="hidden" id="page" value="1" />'
, '<input type="hidden" id="status" value="0" />'
, '</td>'
, '<td>'
, '<button class="layui-btn layui-btn-xs demo-reload layui-hide">重传</button>'
, '<button class="layui-btn layui-btn-xs layui-btn-danger demo-delete">删除</button>'
, '</td>'
, '</tr>'].join(''));
//单个重传
tr.find('.demo-reload').on('click', function () {
obj.upload(index, file);
});
//删除
tr.find('.demo-delete').on('click', function () {
delete files[index]; //删除对应的文件
tr.remove();
uploadListIns.config.elem.next()[0].value = ''; //清空 input file 值,以免删除后出现同名文件不可选
});
tr.find('#totalPage').val(totalPage);
tr.find('#page').val('1');
//默认-1分块上传
tr.find('#status').val('-1');
that.elemList.append(tr);
//只初始化当前进度条,避免将之前100%重新初始化了
element.render('progress', 'progress-demo-' + index); //渲染新加的进度条组件
var fileName = file.name;
fileName = fileName.substr(0, fileName.lastIndexOf('.'));
preview(f, data, obj, file, LENGTH, fileId, fileName, index, that);
}
//如下方法,对于大文件(100多兆)的情况可能无响应
//obj.preview(function (index, file, result) {
//});
}
, done: function (res, index, upload) {
var that = this;
var tr = that.elemList.find('tr#upload-' + index)
, tds = tr.children();
if (res.code == '-1') { //分片上传
//if(res.code == 0){ //上传成功
var pageStr = that.elemList.find('tr#upload-' + index + ' #page').val();
var page = parseInt(pageStr);
var totalPageStr = that.elemList.find('tr#upload-' + index + ' #totalPage').val();
var totalPage = parseInt(totalPageStr);
element.progress('progress-demo-' + index, Math.ceil(page * 100 / totalPage) + '%'); //执行进度条。n 即为返回的进度百分比
page = page + 1;
console.log(page);
that.elemList.find('tr#upload-' + index + ' #page').val(page);
that.elemList.find('tr#upload-' + index + ' #status').val('-1');
}
else if (res.code == '0') { //上传完成
element.progress('progress-demo-' + index, '100%'); //执行进度条。n 即为返回的进度百分比
that.elemList.find('tr#upload-' + index + ' #status').val('0');
tds.eq(3).html(''); //清空操作
//delete this.files[index]; //删除文件队列已经上传成功的文件
return;
}
else { //上传错误
that.elemList.find('tr#upload-' + index + ' #status').val('1');
element.progress('progress-demo-' + index, '0%'); //执行进度条。n 即为返回的进度百分比
}
}
, allDone: function (obj) { //多文件上传完毕后的状态回调
console.log(obj)
}
, error: function () {
//请求异常回调
}
});
//定时执行必须单独设置一个方法,否则会导致只执行最后一个定时
function preview(f, data, obj, file, LENGTH, fileId, fileName, index, that) {
var xx = 'var progressTimer' + f + ' = setInterval(function () {\
uploadxx(data, obj, file, LENGTH, fileId, fileName, index, progressTimer'+ f + ',that)\
}, 100);';
eval(xx);
}
function uploadxx(data, obj, file, len, fileId, fileName, index, progressTimer, that) {
console.log(index);
var pageStr = that.elemList.find('tr#upload-' + index + ' #page').val();
var totalPageStr = that.elemList.find('tr#upload-' + index + ' #totalPage').val();
var status = that.elemList.find('tr#upload-' + index + ' #status').val();
var page = parseInt(pageStr);
var totalPage = parseInt(totalPageStr);
if (totalPage == page && (parseInt(status) == '0' || parseInt(status) == '1')) {
clearInterval(progressTimer);
console.log('clear');
}
else {
if (status == '-1') {
that.elemList.find('tr#upload-' + index + ' #status').val('-2');
data.fileId = fileId;
data.fileName = fileName;
data.fileSourceId = '<% =Guid.NewGuid()%>';
data.fileSourceFlag = "Form";
data.fileType = 'Test';
data.page = page;
data.totalPage = totalPage;
obj.upload(index, file.slice((page - 1) * len, page * len));
}
}
}
这样基本是实现了,不过我还想继续封装下,简化开发。
Layui上传系列之三(插件封装,简化开发)_龙井茶的Sky-CSDN博客https://blog.csdn.net/to_love_/article/details/120660380