webuploader上传大文件

时间:2021-04-14 18:04:01 类型:PHP
字号:    

大文件上传, 在web应该用中是一个常规应用, 然因为服务器配置设置最大上传大小, 或者上传时间太久没有进度条等等, 都会给我们的使用带来较大的困扰, 这里基于layui进度条+thinkphp6.0+webuploader的实现, 分享给大家一个完整的案例

1, html代码:

<script type="text/javascript" src="__STATIC__/js/jquery.js"></script>
<!--引入 上传大文件 用的插件 webuploade 的相关文件-->
<link rel="stylesheet" type="text/css" href="__STATIC__/webuploader/webuploader.css">
<!--引入JS-->
<script type="text/javascript" src="__STATIC__/webuploader/webuploader.js"></script>
<div class="layui-form-item">
           <label class="layui-form-label">上传大文件</label>
           <div class="layui-input-block">
                  <div id="uploader" class="wu-example">
                     <!--用来存放文件信息-->
                     <div id="thelist" class="uploader-list"></div>
                    <div class="btns">
                       <div id="pickerfile" style="float:left;">选择文件</div>
                       <button id="startup" type="button" class="btn-default layui-btn" style="height: 44px; margin-left:10px; display: none;">开始上传</button>
                   </div>
                  <table class="layui-table">
                      <thead>
                      <tr>
                          <th>文件名</th>
                          <th>文件大小</th>
                          <th>文件验证</th>
                          <th style="width: 300px;">进度</th>
                          <th>操作</th>
                      </tr>
                      </thead>
                      <tbody id="fileinfo">
                      </tbody>
                  </table>
                      <div id="infos" style="display:none;">
                      </div>
                 </div>
           </div>
 </div>

2, JS代码

   引入layui

<script src="__LAYUIADMIN__/layui/layui.js"></script>

   

function formatFileSize(size){
    var fileSize =0;
    if(size/1024>1024){
        var len = size/1024/1024;
        fileSize = len.toFixed(2) +"MB";
    }else if(size/1024/1024>1024){
        var len = size/1024/1024;
        fileSize = len.toFixeds(2)+"GB";
    }else{
        var len = size/1024;
        fileSize = len.toFixed(2)+"KB";
    }
    return fileSize;
}

layui.config({
 base: '__LAYUIADMIN__/' //静态资源所在路径
}).extend({
  index: 'lib/index' //主入口模块
}).use(['index', 'form', 'laydate','upload','element'], function(){
  var $ = layui.$
  ,admin = layui.admin
  ,element = layui.element
  ,layer = layui.layer
  ,laydate = layui.laydate
  ,form = layui.form
  ,upload = layui.upload;
  
  var uploader = WebUploader.create({
    // swf文件路径
    swf: '__STATIC__/webuploader/Uploader.swf',
    // 文件接收服务端。
    server: '{:url("@qile/upload/getBlockFile")}',
    // 选择文件的按钮。可选。
    // 内部根据当前运行是创建,可能是input元素,也可能是flash.
    pick: {
        "id":'#pickerfile',
        "multiple":true   //禁止多选。
    },
    chunked: true,           //开启分片上传
    chunkSize: 1 * 1024 * 1024,  //每一片的大小
    chunkRetry: 5,         // 如果遇到网络错误,重新上传次数
    threads: 3,  //上传并发数。允许同时最大上传进程数。
    // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
    resize: false,
    // 选完文件后,是否自动上传。
    auto: false,
    // 只允许选择图片文件。
    // accept: {
    //     title: 'Images',
    //     extensions: 'gif,jpg,jpeg,bmp,png',
    //     mimeTypes: 'image/*'
    // }
});


// 当有文件被添加进队列的时候
uploader.on('fileQueued', function( file ) {
    $("#startup").hide(); //隐藏开始上传按钮 , 待全部检验完再显示
    let size = formatFileSize(file.size);
    //根据文件大小定义id名字

    let progress = "<div class='layui-progress' lay-showPercent=\"yes\">" +
        "<div class='layui-progress-bar' lay-percent=''></div>" +
        "</div>";
    let html = "<tr id='"+ file.id +"'>" +
                    "<td>"+file.name+"</td>" +
                    "<td>"+size+"</td>" +
                    "<td class='md5'></td>" +
                    "<td class='state'>" + progress+ "</td>" +
                    "<td class='do'></td>" +
                "</tr>";
    $("#fileinfo").append(html);
    element.render('progress');
    uploader.md5File(file).progress(function (percentage) {
            // 及时显示进度
            //console.log("测试进度");
            let v = parseInt(percentage * 100);
            $("#"+file.id).find(".md5").text(v+"%");
        }).then(function (fileMd5) {
            $("#"+file.id).find(".do").text('等待上传...');
            file.md5 = fileMd5;
            var all_f = uploader.getFiles(); //队列中的所有的文件
            var nums = uploader.getFiles().length;
            var count = 0;
            for(var i = 0; i < nums; i++){
                if(all_f[i].hasOwnProperty('md5')) count++;
            }
            if(nums == count){
                //当队列中的每一个文件都有了 md5 属性时, 开启文件上传按钮
                $("#startup").show();
            }
            //计算大的文件是需要一段时间的

        });
    //file : 代表队列中的各个文件
});

// 每个分块发送前检查,并附加MD5数据
uploader.on('uploadBeforeSend', function(block, data ) {
    data.md5 = block.file.md5;
    //data.status = block.file.status;
});

// 上传提交
$("#startup").on('click', function() {
    uploader.upload();
});

// 文件上传过程中创建进度条实时显示。
uploader.on( 'uploadProgress', function( file, percentage ) {
    var id = "#" + file.id;
    var value = parseInt(percentage * 100)+"%";
    $(id).find(".layui-progress-bar").attr({"lay-percent":value});
    $(id).find('.do').text('上传中');
    element.render('progress');
});


/*uploader.on( 'uploadSuccess', function( file ) {
    $( '#'+file.id ).find('.do').text('已上传');
});

uploader.on( 'uploadError', function( file ) {
    $( '#'+file.id ).find('.do').text('上传出错');
});*/

// 上传完成后触发
uploader.on('uploadSuccess', function (file,response) {
    $.post('{:url("@qile/upload/merge")}', { md5: file.md5, fileName: file.name }, function (obj) {
        if (obj.status) {
            //将上传的文件用 input做记录, 方便提交到数据库中
            var data  = {md5: file.md5, source_name: file.name, size:file.size,save_name:obj.save_name};
            var str_data = JSON.stringify(data);
            var html = "<input type='text' class='info' name=\"info[]\" _id='"+file.id+"' value='"+str_data+"'>";
            $("#infos").append(html);
            //更新设置 状态
            var btn = '<button type="button" class="layui-btn layui-btn-sm layui-btn-danger big_del" _id="'+file.id+'" _save_name="'+obj.save_name+'">删除</button>';
            $("#"+file.id).find(".do").html(btn);
        }
    });
});
//上传完成
//大文件上传结束
//删除
$(document).on("click",".big_del",function(){
    let _this = $(this);
    layer.confirm('确定要删除吗?', {icon: 3, title:'提示', title:""}, function(index_alert){
        var _id = _this.attr("_id");
        var data = {files:_this.attr("_save_name")}
        $.post('{:url("@qile/upload/del")}',data);
        _this.parent().parent("tr").remove(); //删除本行
        $("input[_id='"+_id+"']").remove();     //删除
        layer.close(index_alert);
    })

})
//删除结束
});

3, 控制器接收文件

class UploadController
{
    
    //接收大文件分开块状文件
    public function getBlockFile(){
        set_time_limit(0);
        // 建立临时目录存放文件-以MD5为唯一标识
        $post = request()->post();

        $dir = str_replace("\\","/",public_path()) . "static/upload/" . $post["md5"];
        if (!file_exists($dir)) {
            mkdirs($dir,0777);
        }
        // 移动每一块文件到 唯一 标识文件夹下
        if($post["size"] >= 1 * 1024 * 1024){
            move_uploaded_file($_FILES["file"]["tmp_name"], $dir.'/'.$post["chunk"]);
        }
        else{
            //文件 小于 设置的 chunk 大小 时,没有chunk
            $file = $_FILES["file"];
            move_uploaded_file($_FILES["file"]["tmp_name"], $dir.'/'.$file["name"]);
        }

    }
    //合成分块上传的文件
    public function merge(){
        set_time_limit(0);
        // 接收相关数据
        $post = $_POST;
        $path = str_replace("\\","/",public_path()) . "static/upload/";
        // 找出分片文件
        $dir = $path . $post["md5"];
        // 获取分片文件内容
        $block_info = scandir($dir);
        // 除去无用文件
        foreach ($block_info as $key => $block) {
            if ($block == '.' || $block == '..') unset($block_info[$key]);
        }
        // 数组按照正常规则排序
        natsort($block_info);
        // 定义保存文件
        $save_path = date("Ymd").'/';
        $target_path  = $path . $save_path;
        if(!is_dir($target_path)) mkdirs($target_path);
        $new_file_name = date('Ymdgis').rand().".".getSuffix($post['fileName']);
        $save_file = $target_path . $new_file_name;
        $save_name = $save_path . $new_file_name;
        // 没有?建立
        if (!file_exists($save_file)) fopen($save_file, "w");
        // 开始写入
        $out = @fopen($save_file, "wb");
        // 增加文件锁
        if (flock($out, LOCK_EX)) {
            foreach ($block_info as $b) {
                // 读取文件
                if (!$in = @fopen($dir.'/'.$b, "rb")) {
                    break;
                }

                // 写入文件
                while ($buff = fread($in, 4096)) {
                    fwrite($out, $buff);
                }

                @fclose($in);
                @unlink($dir.'/'.$b);
            }
            flock($out, LOCK_UN);
        }
        @fclose($out);
        @rmdir($dir);
        return json(["save_name" => $save_name, "status" => 1]);
    }

    //删除提交的文件
    public function del(){
        $files = request()->post("files");
        if($files){
            $path = str_replace("\\","/",public_path()) . "static/upload/";
            if(file_exists($path.$files)){
                unlink($path.$files);
            }
        }
    }
}

4. 实例效果图

   1.jpg

<