使用 layui upload 和 PHP 实现分段上传文件 + 进度条

分享 已结 精帖
52 9779
Lennon280
Lennon280 2017-12-26
悬赏:20飞吻
之前的 /layui/lay/modules/upload.js 修改代码帖子里面不见了,可能我的操作失误!。

重新上传一下帖子
layer 上传控件修改
功能:利用JS分割文件大小上传
该功能修改过modules中的upload.js文件 修改地方是u变量 /layui/lay/modules/upload.js
和layer 实例:多文件列表示例 JS(已修改过)

html:
html代码:
<div class="layui-form-item">
<label class="layui-form-label">上传图标</label>
<div class="layui-input-block">
<div class="layui-upload">
<button type="button" class="layui-btn layui-btn-normal" id="testList">选择多文件</button>
<div class="layui-upload-list">
<table class="layui-table">
<thead>
<tr><th>文件名</th>
<th>大小</th>
<th>状态</th>
<th>操作</th>
</tr></thead>
<tbody id="demoList"></tbody>
</table>
</div>
<button type="button" class="layui-btn" id="testListAction">开始上传</button>
</div>
</div>
<div id="upload_img_list"></div>
</div>
upload.js 自定义的js
upload.js 自定义的js

//Js 数据容量单位转换(kb,mb,gb,tb)
function bytesToSize(bytes) {
if (bytes === 0) return '0 B';
var k = 1000, // or 1024
sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
i = Math.floor(Math.log(bytes) / Math.log(k));

return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];
}


/*
上传参数设定
*/

var upurl = "{:url('Common/upimg',['author'=>'Ycl_24'])}";//上传图片地址
var duotu = true;//是否为多图上传true false


//多文件列表示例

layui.use('upload', function(){
var $ = layui.jquery
,upload = layui.upload;


var load = '';

//多文件列表示例
var demoListView = $('#demoList');
uploadListIns = upload.render({
elem: '#testList'
,url: upurl
,accept: type
,multiple: duotu
,auto: false
,bindAction: '#testListAction'
,size: 512000
,processData: false
,contentType: false
,before: function(obj) {
load = top.layer.load(2, {content:'正在上传...',shade: [0.001, '#393D49'],success: function(layero){
layero.find('.layui-layer-content').css({'padding-left':'40px','width':'100px','padding-top':'5px'});
layero.css({'border-radius':'0','background':'white','box-shadow':'1px 1px 37px rgb0.value = ''; //清空 input file 值,以免删除后出现同名文件不可选
});

/*文件上传判断,多选:文件不能重复,单选:只能选择一个*/
if(duotu){
var trUpload = demoListView.find('tr.'+file.lastModified).html(); //判断文件上传是否重复
if(trUpload){
layer.msg('该选中文件,列表中已存在', {icon:2 , time: 2000 });
}else{
demoListView.append(tr);
}
}else{
demoListView.html(tr);
}

});
}
,done: function(res, index, upload){
// layer.close(layer.msg());//关闭上传提示窗口

if(res.code == 0){ //上传成功
console.log(res.progress);
var tr = demoListView.find('tr#upload-'+ index)
,tds = tr.children();
if(res.progress == '100'){
top.layer.close(load);//关闭上传提示窗口
tds.eq(2).html('<span style="color: #5FB878;">上传成功</span>');
tds.eq(3).html('<a href="'+res.tolink+'" target="_blank" class="layui-btn layui-btn-mini layui-btn-normal">查看</a>');
if (duotu == true) {//调用多图上传方法,其中res.imgid为后台返回的一个随机数字

$('#upload_img_list').append('<input type="hidden" name="file_info[]" value="' + res.tolink + '" />');

}else{//调用单图上传方法,其中res.imgid为后台返回的一个随机数字

$('#upload_img_list').html('<input type="hidden" name="file_info" value="' + res.tolink + '" />');

}
}else{
tds.eq(2).html('<div class="layui-progress layui-progress-big" lay-showpercent="true"><div class="layui-progress-bar" lay-percent="'+res.progress+'%" style=" width: '+res.progress+'%;"><span class="layui-progress-text">'+res.progress+'%</span></div></div>');
tds.eq(3).html(''); //清空操作
}


return delete this.files[index]; //删除文件队列已经上传成功的文件
}
this.error(index, upload,res.info);
}
,error: function(index, upload,info){
// layer.close(layer.msg());//关闭上传提示窗口
top.layer.close(load);//关闭上传提示窗口
var tr = demoListView.find('tr#upload-'+ index)
,tds = tr.children();
tds.eq(2).html('<span style="color: #FF5722;">上传失败.'+info+'</span>');
tds.eq(3).find('.demo-reload').removeClass('layui-hide'); //显示重传
}
});


});
/layui/lay/modules/upload.js 找到 p.prototype.upload 这个,修改里面的U变量

u = function() {
var t = 0,
n = 0,
a = e || o.files || o.chooseFiles || r.files,
u = function() {
l.multiple && t + n === o.fileLength && "function" == typeof l.allDone && l.allDone({
total: o.fileLength,
successful: t,
aborted: n
})
};

layui.each(a,
function(e, a) {

var start = 0; //文件开始长度
var end; //文件结束长度
var index = 0; //第几个切片
var filesize = a.size; //文件大小
var filename = a.name; //文件名称
var bytesPerPiece = 1024 * 1024; // 每个文件切片大小定为1MB .
//计算文件切片总数
var totalPieces = Math.ceil(filesize / bytesPerPiece);
var timer = 0;
up();
function up(){
if(start < filesize) {
end = start + bytesPerPiece;
if(end > filesize) {
end = filesize;
}

var chunk = a.slice(start,end);//切割文件
var sliceIndex= a.name + index;
var r = new FormData;
r.append(l.field, chunk, filename);
r.append('totalPieces', totalPieces); //提交给后台PHP接收 切片总数 (之前在這里处理进度条,发现有问题)
r.append('index', index); //提交给后台PHP接收 切片第几个

layui.each(l.data,
function(e, i) {
r.append(e, i)
}),
i.ajax({
url: l.url,
type: l.method,
data: r,
contentType: !1,
processData: !1,
dataType: "json",
success: function(i) {
start = end;
index++;
t++,
timer = setTimeout(function(){
up();
}, 1000);
d(e, i),
u()
},
error: function() {
n++,
o.msg("请求上传接口出现异常123"),
m(e),
u()
}
})

}else{
clearTimeout(timer);

return;
}
}


})
},
php代码
php代码:
public function upimg(){

$file = $_FILES['file']; //上传文件file

$totalPieces = $_POST['totalPieces']; //上传文件切片总数

$index = $_POST['index']; //上传文件当前切片

$progress = round(($index/$totalPieces),2)*100;

if($index == ($totalPieces - 1)){

$progress = 100; //进度条
}

$filename_arr = explode('.', $file['name']); //获取上传文件名称

$ext = end($filename_arr);

$date = date("Ymd");

$savePathName = md5($date.$file['name']).'.'.$ext; //上传文件名称加密

$path = ROOT_PATH . 'public' . DS . 'upload'. DS .'images'. DS .$date; // 根目录下的 public/upload/images/当前日期

$savePath = ROOT_PATH . 'public' . DS . 'upload'. DS .'images'. DS .$date. DS .$savePathName;

$tolink = "/public/upload/images/".$date.'/'.$savePathName; //返回根目录下的图片名称

if (!file_exists($path)){
mkdir($path);
}

$return = [
'info' => '上传失败!',
'tolink' => $tolink,
'imgid' => $savePathName,
'code' => 1,
'error' =>$file['error'],
];
if($file['error']==0){
if(!file_exists($savePath)){
if(move_uploaded_file($file['tmp_name'],$savePath)){
$return = [
'info' => '上传成功!',
'tolink' => $tolink,
'imgid' => $savePathName,
'code' => 0,
'progress' => $progress,
];
}
}else{
$content=file_get_contents($file['tmp_name']);
if (file_put_contents($savePath, $content,FILE_APPEND)) {
$return = [
'info' => '上传成功!',
'tolink' => $tolink,
'imgid' => $savePathName,
'code' => 0,
'progress' => $progress,
];
}
}
}
$this->YCL24_AJAX_MSG($return); // 這里 您可以换成 echo json_encode(value);

}
图片展示:






回帖
  • [微笑] ,吊炸天,老司机开车了。。。快上车。。
    3 回复
  • lian
    2017-12-26
    [good] 最近正好再用
    3 回复
  • 超人
    2017-12-26
    @超人 php是最好的语言[阴险]
    3 回复
  • 看到那个进度,我就决定收藏了!
    2 回复
  • 先收藏了[心]
    1 回复
  • Pojin
    2017-12-26
    收藏了,非常好!
    1 回复
  • 且试天下
    2017-12-26
    php是最好的语言[阴险]
    1 回复
  • Akun
    2017-12-26
    [哈哈] PHP!!!
    1 回复
  • @Mistyss 这里不是非压缩版本吗?https://github.com/sentsin/layui/tree/master/src
    1 回复
  • Lennon280
    2017-12-26
    @Mistyss 非压缩版本的我没试过,你可以下载最新的不是非压缩版本的,modules中的upload.js文件里面的,你直接在线格式化一下,去搜索p.prototype.upload
    1 回复
  • Mistyss
    2017-12-26
    @Lennon280 嗯。我现在就是用你的方法,直接官网下2.2.45
    找到upload.js然后https://tool.lu/js/ 在线工具 解密,就找到了p.prototype.upload 这个了
    已经OK了。在改代码了
    1 回复
  • A_彬
    2017-12-26
    看看看看。
    1 回复
  • 星期日
    2017-12-26
    @Lennon280 厉害厉害
    1 回复
  • Mistyss
    2017-12-26
    大哥,你的非压缩版在那里下的呀
    0 回复
  • Lennon280
    2017-12-26
    @Mistyss layui 都是在官网下的最新版,如果你是说modules中的upload.js文件里面的,你直接在线格式化一下就可以了。
    0 回复
  • Mistyss
    2017-12-26
    @Lennon280 ok,懂了。
    我以为有地方直接下载非压缩版本,原来直接用在线工具,解压缩解密的
    0 回复
  • Mistyss
    2017-12-26
    @南无乐乐 好把。没找到你这个

    我在官方文档里面找到的是这样的连接
    https://gitee.com/sentsin/layui
    https://github.com/sentsin/layui/

    下载后都是源码。没找到LZ说的这个p.prototype.upload = function(e, t)
    0 回复
  • Lennon280
    2017-12-26
    可以把"正在上传"提示放在before中
    //多文件列表示例

    layui.use('upload', function(){
    var $ = layui.jquery
    ,upload = layui.upload;


    var load = '';

    //多文件列表示例
    var demoListView = $('#demoList');
    uploadListIns = upload.render({
    elem: '#testList'
    ,url: upurl
    ,accept: 'file'
    ,multiple: duotu
    ,auto: false
    ,bindAction: '#testListAction'
    ,size: 512000
    ,processData: false
    ,contentType: false
    ,before: function(obj) {
    load = top.layer.load(2, {content:'正在上传...',shade: [0.001, '#393D49'],success: function(layero){
    layero.find('.layui-layer-content').css({'padding-left':'40px','width':'100px','padding-top':'5px'});
    layero.css({'border-radius':'0','background':'white','box-shadow':'1px 1px 37px rgb0.value = ''; //清空 input file 值,以免删除后出现同名文件不可选
    });

    demoListView.append(tr);
    });
    }
    ,done: function(res, index, upload){
    // layer.close(layer.msg());//关闭上传提示窗口

    if(res.code == 0){ //上传成功
    console.log(res.progress);
    var tr = demoListView.find('tr#upload-'+ index)
    ,tds = tr.children();
    if(res.progress == '100'){
    top.layer.close(load);//关闭上传提示窗口
    tds.eq(2).html('<span style="color: #5FB878;">上传成功</span>');
    tds.eq(3).html('<a href="'+res.tolink+'" target="_blank" class="layui-btn layui-btn-mini layui-btn-normal">查看</a>');
    }else{
    tds.eq(2).html('<div class="layui-progress layui-progress-big" lay-showpercent="true"><div class="layui-progress-bar" lay-percent="'+res.progress+'%" style=" width: '+res.progress+'%;"><span class="layui-progress-text">'+res.progress+'%</span></div></div>');
    tds.eq(3).html(''); //清空操作
    }


    return delete this.files[index]; //删除文件队列已经上传成功的文件
    }
    this.error(index, upload,res.info);
    }
    ,error: function(index, upload,info){
    // layer.close(layer.msg());//关闭上传提示窗口
    top.layer.close(load);//关闭上传提示窗口
    var tr = demoListView.find('tr#upload-'+ index)
    ,tds = tr.children();
    tds.eq(2).html('<span style="color: #FF5722;">上传失败.'+info+'</span>');
    tds.eq(3).find('.demo-reload').removeClass('layui-hide'); //显示重传
    }
    });


    });
    0 回复
  • Lennon280
    2017-12-26
    补充:

    上传控件://多文件列表示例
    是多文件上传,但设置了 multiple: duotu ,duotu 如果为false,只是在选择文件的时候不能多选,true可多选。

    但想要的效果是:
    false 单选,只能上传一个并且上传列表里面只能有一个
    true 多选,上传列表多个,并且不能重复

    修改自定义 上传js代码


    ,choose: function(obj){   
    // layer.close(layer.msg());//关闭上传提示窗口
    var files = this.files = obj.pushFile(); //将每次选择的文件追加到文件队列
    //读取本地文件
    obj.preview(function(index, file, result){

    var tr = $(['<tr id="upload-'+ index +'" class="'+file.lastModified+'">'
    ,'<td>'+ file.name +'</td>'
    ,'<td>'+ bytesToSize(file.size) +'</td>'
    ,'<td>等待上传</td>'
    ,'<td>'
    ,'<button class="layui-btn layui-btn-mini demo-reload layui-hide">重传</button>'
    ,'<button class="layui-btn layui-btn-mini 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 值,以免删除后出现同名文件不可选
    });

    /*文件上传判断,多选:文件不能重复,单选:只能选择一个*/
    if(duotu){
    var trUpload = demoListView.find('tr.'+file.lastModified).html(); //判断文件上传是否重复
    if(trUpload){
    layer.msg('该选中文件,列表中已存在', {icon:2 , time: 2000 });
    }else{
    demoListView.append(tr);
    }
    }else{
    demoListView.html(tr);
    }

    });
    }
    0 回复
  • Django8
    2017-12-26
    [可爱]
    0 回复
  • Django8
    2017-12-26
    [为空]
    0 回复
  • Django8
    2017-12-26
    吃的是草
    0 回复
  • 王华
    2017-12-27
    赞赞赞[good] [good] [good]
    0 回复
  • 超人
    2017-12-27
    0 回复
  • 步尘
    2017-12-27
    惊现前段大佬一名,,这js我看不懂,,[衰]
    0 回复
  • Lennon280
    2017-12-27
    @步尘 我想说我是php[思考] ,这个分段上传是参考JS原生的分割上传模仿的
    0 回复
  • 步尘
    2017-12-27
    @Lennon280 这年头后台都开始转前端了,,我们项目组五个后台,三个已经开始自学vue了,,包括我,然后其他组的一个后台,这两天要开技术交流会,讲JavaScript,,[哈哈]
    0 回复
  • 再加上预览就更好了[哈哈]
    0 回复
  • Lennon280
    2017-12-28
    @虽远必诛 补充说明里面加了,查看功能。预览加可以,目前还没想到放在什么地方,如果只上传图片的话可以加上,如果是文件压缩包就不好弄。
    0 回复
  • Lennon280
    2017-12-28
    @步尘 我都想转py.哈哈,但我觉得目前还是小菜手,没有真正了解PHP语言。
    0 回复
本帖已设置禁止回复