由于项目需要上传超大文件,当然现在的条件好了,1-3百M的文件没多大问题,但是超过1G的还是有问题的。(当然oss单个文件最高可以5g)对于大额文件上传存在上传缓慢甚至失败的问题
所有研究了一下layui怎么实现分块上传(layui没有提供,不过大部分需求满足所有,还有各家的云储存减少一定需求)。
实现逻辑就是利用ajax技术做循环提交文件。
先把文件对象(二进制)分割成多分,然后分多次提交
后端接受后,在合成一个完整的文件。大致流程如此,实现的方式有差异
前端代码:(哈哈,半吊子前端,也是参考别人的)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>layui</title> <meta name="renderer" content="webkit"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <!-- 注意:如果你直接复制所有代码到本地,上述css路径需要改成你本地的 --> <?php include get_theme_path() . 'commom/head.php' ?> <style> .layui-progress { position: fixed; width: 300px; left: 50%; margin-left: -150px; top: 200px; height: 18px; border-radius: 10px; background: #fff; z-index: 99999999; overflow: hidden; display: none; } </style> </head> <body> <form class="layui-form" method="post" action=""> <input type="hidden" name="form_submit" value="ok"/> <div class="layui-form-item"> <label class="layui-form-label">选择文件:</label> <div class="layui-input-block"> <button type="button" class="layui-btn" id="file"><i class="layui-icon"></i>上传文件</button> </div> </div> <div class="layui-form-item"> <label class="layui-form-label">文件名:</label> <div class="layui-input-block"> <input type="text" name="name" id="name" value="" lay-verify="title" autocomplete="off" readonly="true" class="layui-input"> </div> </div> <div class="layui-form-item layui-form-text"> <label class="layui-form-label">文件地址:</label> <div class="layui-input-block"> <input type="text" name="path" id="path" value="" lay-verify="path" autocomplete="off" readonly="true" placeholder="" class="layui-input"> </div> </div> <div class="layui-form-item"> <div class="layui-input-block"> <button class="layui-btn" lay-submit="" lay-filter="submit">立即提交</button> <button type="reset" class="layui-btn layui-btn-primary">重置</button> </div> </div> </form> <div class="layui-progress layui-progress-big" lay-showpercent="true" lay-filter="Progress"> <div class="layui-progress-bar layui-bg-blue" lay-percent="0%"></div> </div> <!-- 注意:如果你直接复制所有代码到本地,上述js路径需要改成你本地的 --> <?php include get_theme_path() . 'commom/foot.php' ?> <script> layui.use(['form', 'upload', 'element'], function () { let form = layui.form , upload = layui.upload , element = layui.element , $ = layui.jquery; let number, status, size, totalNum;// number控制数量,status状态,size 文件大小 totalNum总数 upload.render({ elem: '#file', url: apiDomain + '/test/file', //处理上传文件接口 accept: 'file', auto: false, choose: function (obj) { layer.load(1,{shade: 0.3}); element.progress('Progress', '0%'); $('.layui-progress').show(); let data = this.data;//请求上传接口的额外参数 // let files = obj.pushFile(); let LENGTH = 1024 * 1024 * 5; //每片文件大小 obj.preview(function (index, file, result) { size = file.size; totalNum = Math.ceil(size / LENGTH); number = 1;//初始化 status = 1;//初始化 1开始 let fileName = file.name; $('#name').val(fileName); let fileExt = fileName.substring(fileName.lastIndexOf('.') + 1); // let uuidFileName = fileName.substring(0, fileName.lastIndexOf('.')); let uuidFileName = ''//前端自己实现唯一或者后端实现就不用传参, let progress = setInterval(function () { if (totalNum === number && (status === 2 || status === -1)) {//当总数和分片数相等 clearInterval(progress);//结束循环 } else { if (status === 1) { status = 0;//改为0避免直接循环 一次循环一次返回结果避免异步或者后置 data.fileName = uuidFileName; data.number = number; data.totalNum = totalNum; data.fileExt = fileExt; data.type = file.type; let blob = file.slice((number - 1) * LENGTH, number * LENGTH)//分割file(二进制blob)对象,但类似分页找到目标blob对象 obj.upload(index, blob); } } }, 100); }); }, done: function (res) { if (res.code === 100) { element.progress('Progress', Math.ceil(number * 100 / totalNum) + '%');//计算进度条 number = number + 1; status = 1;//分片成功修改状态 } else if (res.code === 200) { //上传完成 element.progress('Progress', '100%'); status = 2; $('#path').val(res.upload); layer.closeAll('loading'); //关闭loading layer.msg('上传成功', {time: 1000, anim: 0}, function () { $('.layui-progress').hide(); }); } else { //上传错误 status = -1; layer.closeAll('loading'); //关闭loading element.progress('Progress', '0%'); if (typeof (res.upload) == "undefined") { } else { $('#path').val(res.upload); } layer.msg("上传失败,请重试", {time: 1500, anim: 0}, function () { $('.layui-progress').hide(); }); } }, error: function () { $('.layui-progress').hide(); } }); }); </script> </body> </html>
可以用循环,for 来控制次数,也可以用递归 当然我这里用的 setInterval 定时器 更好些可以更好的控制
后端代码:
$code=100; // 用原生或者自己session实现 uuid自己实现或者原生(原生的有误差) if (empty(Session::get('fileUid'))){ $uuid = create_guid(); Session::set('fileUid',$uuid); } else { $uuid = Session::get('fileUid'); } //占位符 upload的目录路径自己选 $filename = sprintf('./upload/%s.%s',$uuid,$_POST['fileExt']); $data = file_get_contents($_FILES['file']['tmp_name']); if($_POST['number']==1){ file_put_contents($filename,$data);//先生成一个文件 } else { //其余文件追加到文件末尾 file_put_contents($filename,$data,FILE_APPEND); } //最后一片文件 if($_POST['totalNum']==$_POST['number']){ Session::delete('fileUid'); $code=200; } $res=['code'=>$code,'upload'=>$filename]; echo json_encode($res); }
标签:分块,layui,number,upload,let,file,progress From: https://www.cnblogs.com/yangshiyi/p/16940770.html