• 实习工作记录(一)大文件上传vue+WebUploader


    说说我实习前端开发的时候用的大文件上传,前端原本项目用的是element自带的el-upload文件上传,确实很方便,element把数据上传成功,失败,上传中等等的监听事件都已经封装好了,文件列表和文件信息也携带在监听方法的参数中,调用然后打印,,一目了然,进行业务逻辑开发效率很高。但问题是,,element的upload没有附带大文件的断点续传功能,上传过程中如果中断那么就比较麻烦,所以需要自己开发。

    什么是断点续传?

    当用户上传文件过程中如果由于网络,,手滑,,或者其他骚操作等种种原因突然中断上传,那原本上传一半的文件要怎么处理???下次上传这个文件还得全部重来??这样是很浪费性能和资源的。并且,http协议和Springboot都限制了文件的上传大小,文件太大怎么办??这个时候,大文件的断点续传技术完美解决。

    它将一个文件切割成若干部分分开向服务器端上传,每个小的部分我们称为切块,每上传结束一个切块除了保存文件信息,还会在后端保存切块的“”识别码”,用于识别文件上传到哪儿了,等到下次上传时,直接从这个位置开始继续上传,这样大大节省了开销。而且还有一个亮点,如果文件已经上传过,那么可以对后台进行秒传,节省大量时间,用户体验也大大提高。

    想要实现大文件断点续传,我们只需要安装一个插件WebUploader,然后在前端js代码中触发监听,配置相关的变量就可以实现断点续传了,灵活性很高。。。

    插件的底层源码是用JQuery封装的,所以需要安装JQuery

    如果是vue项目,npm安装到环境中:

    npm install JQuery
    npm install WebUploader

    然后在vue页面中引入:

    import $ from Jquery
    import webUploader from WebUploader 

    然后就可以在项目中触发对应的方法和配置了。。

    WebUploader分为三个部分:
    1.注册三个事件,文件上传前,分片上传前和分片上传后,创建WebUploader实例对象,配置文件块大小,上传地址,文件限制大小等变量。。
    2.先判断是否上传过该文件,调用接口。如果上传过,进行秒传,没有则进行切块。
    3.切块后进行分片上传,获取分片的编号,确认分片,
    4.等全部分片上传完成后向后端请求合并分块,成一个完整的文件。

    可以在监听方法中写自己想要的功能代码。。。并且在上传中还包含了进度条信息可以看进度。。
    下面贴上完整前端代码,自行修改请求路径,也可以修改文件的上传配置,可以修改样式,。。。
    (script文件需要引入)
    <!DOCTYPE html>
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <script type="text/javascript" src="jquery.js"></script>
        <script type="text/javascript" src="webuploader.min.js"></script>
        <link href="webuploader.css" type="css/text"/>
        <style type="text/css">
            .test-bar {
                height: 100%;
                background-color: #0b0e9e;
            }
            input{
    
                height: 0px;
            }
    
        </style>
    </head>
    <body>
    
    <div class="uploadWrapper">
        <div class="btnUpload">
            <div id="picker" class="form-control-focus">点击选择文件上传</div>
        </div>
        <div id="thelist" class="uploader-list">
    
        </div>
        <button id="btnSync" type="button" class="btn btn-warning">开始同步</button>
    </div>
    
    <script>
    
    
        //项目ip
        function getRootPath(){
            return("http://localhost:53021");
    
        }
    
        getRootPath();
    
        $("#btnSync").hide();
        //该map,用于给uploader.options.formData表达赋一个动态的键值对.
        var map = {};
        // var testMd5;
        //定义文件的分片大小
        var chunkSize = 0.9 * 1024 * 1024;
    
    
        //监听分块上存过程中的三个时间点
        WebUploader.Uploader.register({
                "before-send-file": "beforeSendFile",//整个文件上存前,触发方法beforeSendFile
                "before-send": "beforeSend",//每个分片上存前,触发方法beforeSend
                "after-send-file": "afterSendFile",//分片上存完毕后,触发方法afterSendFile
            },
            {
                //时间点1:所有分块进行上存之前触发该方法,即当每个文件开始上传第一个分块前就调用该方法
                beforeSendFile: function (file) {
                    console.log("执行时间点1的方法。。。。。");
                    //定义一个异步对象
                    var deferred = WebUploader.Deferred();
                    if (!map[file.id]) {
                        deferred.reject();
                        alert("文件解析出错,请刷新后重写上传.");
                        return deferred.promise();
                    }
                    //显示暂停按键并且隐藏删除按键
                    switchButton(file.id);
    
                    //先查看服务器中是否已经有该文件
                    $.ajax({
                        type: "POST",
                        dataType: "json",
                        url:  getRootPath()+"/resource/secondPass",
                        // timeout: 3000,
                        data: {
                            "fileSize": file.size,
                            "fileName": file.name,
                            "md5Val": map[file.id]
                        },
                        success: function (data) {
                            if (data.status === "1") {
                                console.log("............................................输出返回值:" + data.status);
                                deferred.reject();
                                uploader.skipFile(file);
                                //清除进度条,如果是秒传,那么是不会触发方法uploader.on('uploadComplete'
                                fadeOutProgress(file);
                                $('#' + file.id).find("p.state").text("已经上传");
                                deferred.resolve();
                            } else {
                                $('#' + file.id).find("p.state").text("正在上传...");
                                deferred.resolve();
                            }
                        },
                        error: function (data) {
                            deferred.reject();
                            console.log(JSON.stringify(data));
                            $('#' + file.id).find("p.state").text("上传出错...");
                            alert("网络错误,请刷新后再上传");
                        }
                    });
    
                    return deferred.promise();
                },
                //时间点2:如果有分块上传,则每个分块上传之前调用此函数
                //用于文件的续传
                beforeSend: function (block) {
                    console.log("执行时间点2的方法。。。");
                    var deferred = WebUploader.Deferred();
                    $.ajax({
                        type: "POST",
                        dataType: "json",
                        url:  getRootPath()+"/resource/checkChunk",
                        data: {
                            "chunk": block.chunk,
                            //block.file.id,获取该分片对应的文件的id,从而获取该文件的md5值
                            "md5Val": map[block.file.id]
                        },
                        success: function (data) {
                            if (data.status === "1") {
                                console.log("跳过..");
                                //分片存在,跳过
                                deferred.reject();
                            } else {
                                console.log("上传..");
                                //分片不存在,那么就上传.
                                deferred.resolve();
                            }
    
                        },
                        error: function (data) {
                            console.log("时间点2出错:" + JSON.stringify(data));
                            //如果是一般的请求出错,那么也可以尝试上传
                            deferred.resolve();
                        }
                    });
                    return deferred.promise();
    
                },
                //时间点3:一个文件的所有分片上传成功后,调用该方法,让后台合并所有分片
                //该方法的在uploader.on("success")方法前执行。
                afterSendFile: function (file) {
                    $('#' + file.id).find('p.state').text("后台正在合并文件...");
                    //上传成功后,异步请求后台的servlet,发送的数据有guid(该文件所有分片保存的目录),chunks(该文件一共分了多少片,注意要向上取整),filename(文件名)
                    $.ajax({
                        type: "POST",
                        dataType: "json",
                        url:  getRootPath()+"/resource/mergeChunk",
                        data: {
                            "guid": uploader.options.formData.guid,
                            "fileSize": file.size,
                            "chunks": Math.ceil(file.size / chunkSize),
                            "fileName": file.name,
                            "md5Val": map[file.id]
                        },
                        success: function (data) {
                            console.log(JSON.stringify(data))
                            if (data.status === "success") {
                                $('#' + file.id).find('p.state').text('已经上传');
                            } else {
                                $('#' + file.id).find('p.state').text('上传失败');
                            }
                        },
                        error: function (data) {
                            alert(JSON.stringify(data));
                            $('#' + file.id).find("p.state").text("上传出错...");
                        }
                    });
                    console.log("执行时间点3的方法。。。");
    
                }
            });
    
    
        var uploader = WebUploader.create({
    
            // swf文件路径
            swf: 'webuploader/Uploader.swf',
            // 文件接收服务端。
            server:  getRootPath()+'/resource/upload',
            // 选择文件的按钮。可选。
            // 内部根据当前运行是创建,可能是input元素,也可能是flash.
            pick: '#picker',
            compress: null,//图片不压缩
            chunked: true,  //分片处理
            chunkSize: chunkSize, //每片5M
            chunkRetry: 3,//由于网络原因出现的故障,最多允许分片自动重转3次
            threads: 8,//上传并发数。允许同时最大上传进程数。
            fileSizeLimit: 12 * 1024 * 1024 * 1024,//12G 验证文件总大小是否超出限制, 超出则不允许加入队列
            fileSingleSizeLimit: 5 * 1024 * 1024 * 1024,  //5G 验证单个文件大小是否超出限制, 超出则不允许加入队列
            fileNumLimit: 100,
            formData: {
                test: 123
            },
            //禁用全局拖拽功能
            disableGlobalDnd: true,
            // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
            resize: false
        });
        console.log(uploader.upload());
        
    
        // 当有文件被添加进队列的时候触发
        uploader.on('fileQueued', function (file) {
            alert("123");
            console.log(file);
    
            $("#thelist").append(
                '<div id="' + file.id + '" class="item">'
                + '<h4 class="info">' + file.name + '</h4>'
                + '<p class="state">等待上传...</p>'
                + '<button class="btn btn-info btn-stop" style="display:none;">' + '暂停' + '</button>'
                + '<a href="javascript:void(0);" class="btn btn-primary file_btn btnRemoveFile" >' + '删除' + '</a>'
                + '<div class="progress">'
                + '<div id="' + file.id + 'progress"  class="test-bar" style=" 0%;" >'
                + "<span>"
                + "</span>"
                + '</div>'
                + '</div>'
                + '</div>');
            (new WebUploader.Uploader()).md5File(file, 0, 10 * 1024 * 1024).progress(function (percentage) {
                $('#' + file.id).find('p.state').text("正在读取文件信息..." + parseInt(percentage * 100) + "%");
            })
            //当文件读取完后,就执行then方法
                .then(function (md5Val) {
                    map[file.id] = md5Val;
                    $('#' + file.id).find("p.state").text("成功读取文件信息");
                    //uploader.options.formData.file.id=md5Val,这种方式中"file.id"只能作为一个字符串
                    //下面种方式可以给uploader.options.formData表单动态赋一个键值对
                    $.extend(uploader.options.formData, map);
    
                });
            $("#btnSync").show();
            var obj = 'div[id=' + file.id + ']';
    
            console.log("打印id啊:" + $(obj).attr("id"));
            //给“删除”按键绑定监听事件
            $('#' + file.id + " a").bind("click", function () {
                var fileItem = $(this).parent();
                var fileId = $(fileItem).attr("id");
                console.log("删除",map);
    
                if (!map[fileId]) {
                    alert("正在解析文件,请稍后再操作...")
                    return;
                }
                //  console.log("输出id:" +fileId);
                //$(fileItem).attr("id")意思是,获取到fileItem该标签的id属性的值,true为从队列中移除
                uploader.removeFile(file, true);
                //同时取消文件上传
                uploader.cancelFile(file);
                delete map[fileId];
                var len = $('#thelist').children("div").length;
                console.log("删除前的文件列表长度:" + len);
                //渐变的效果的消失
                $(fileItem).fadeOut(function () {
                    $(fileItem).remove();
                });
                //由于上面的remove方法是异步删除,因此当下面获取长度的时候,长度还是不变,因此当长度为1的时候,其实list中就没有文件了
                var len = $('#thelist').children("div").length;
                console.log("打印文件列表长度:" + len);
                if (len === 0||len===1) {
                    $("#btnSync").hide();
                }
            });
            //给“暂停”按键绑定监听事件
            $('#' + file.id + " button").bind("click", function () {
                console.log("暂停");
                clickStopButton(file);
            });
    
        });
    
        //上传过程中,一直会执行该方法
        uploader.on('uploadProgress', function (file, percentage) {
            console.log("当前文件" + file.id + "上传的百分比" + percentage + "
    ");
            //因为percentage是百分比(小数来的),因此要显示进度条效果,就先乘100,然后(percentage*100)%作为进度条的宽度百分比,
            // 就可以实现进度条效果
            // $('#' + file.id).children($("#test-bar")).css("width", parseInt(percentage * 100) + "%");
            $('#' + file.id + 'progress').css("width", parseInt(percentage * 100) + "%");
        });
    
        /**
         * 文件上传成功后,就在该文件对应的位置上,显示上传成功,file.id,作为上传文件位置标签的id,
         */
        uploader.on('uploadSuccess', function (file) {
            switchButton(file.id);
            console.log("执行上传成功的方法。。");
            // $('#' + file.id).find("p.state").text("上传成功。。。");
    
        });
    
        uploader.on('uploadError', function (file) {
            switchButton(file.id);
            $('#' + file.id).find('p.state').text('上传出错...');
        });
    
        //不管所有分片发送成功或者失败都会执行该方法
        uploader.on('uploadComplete', function (file) {
            console.log("执行上传完成的方法");
            // //上传完成就删除进度条
            fadeOutProgress(file);
        });
    
    
        /**
         * 当点击上传文件的时候,就触发该方法
         */
        $("#btnSync").on('click', function () {
            //获取文件列表的长度
            var len = $('#thelist').children("div").length;
            //获取计算出md5的文件数
            var mapSize = Object.keys(map).length;
            //一定要全部文件都计算出md5的值,才能上存
            if (len !== mapSize) {
                alert("文件正在解析,请稍等..");
                return;
            }
            console.log(uploader.upload());
    
            uploader.upload();
    
    
        });
    
        //该方法删除指定文件下的进度条
        function fadeOutProgress(file) {
            $('#' + file.id).find('.progress').fadeOut();
        }
    
        //指定文件下的,“暂停”键,和“删除”按键切换显示状态,
        function switchButton(fileId) {
          var  display= $('#' + fileId + " button").css('display');
          if(display==='none') {
              //暂停键显示
              $('#' + fileId + " button").css("display", "");
              //删除键隐藏
              $('#' + fileId + " a").css("display", "none");
          }else {
              //暂停键隐藏
              $('#' + fileId + " button").css("display", "none");
              //删除键显示
              $('#' + fileId + " a").css("display", "");
          }
        }
    
        //指定文件下,点击了“暂停”,则按键变为“继续”,反之一样
        function clickStopButton(file) {
            var content=$('#' + file.id + " button").text();
            if(content.trim()==="暂停"){
                //暂停上传
                uploader.stop(true);
                console.log("暂停");
                $('#' + file.id + " button").text("继续").addClass("btn-warning");
                //删除键显示
                $('#' + file.id + " a").css("display", "");
            }else if(content.trim()==="继续"){
                console.log("继续");
                //继续上传
                uploader.upload();
                $('#' + file.id + " button").text("暂停").removeClass("btn-warning");
                //删除键隐藏
                $('#' + file.id + " a").css("display", "none");
            }
        }
    
    
        // function testInterval() {
        //     var a = 0;
        //     var flag = setInterval(function () {
        //         a = a + 1;
        //         if (a === 5) {
        //             clearInterval(flag);
        //         }
        //         console.log("计时器。。。" + a)
        //     }, 1000);
        //
        //
        // }
    
    
    </script>
    </body>
    </html>
    
    
    
     
  • 相关阅读:
    yarn之安装依赖包
    Yarn 的工作流-创建一个新项目
    yarn使用
    yarn安装
    用yarn替代npm
    搭建开发环境
    网页瞬间转换成桌面应用级程序(IOS/Win/Linux)
    [转]js模块化编程之彻底弄懂CommonJS和AMD/CMD!
    Node.js模块导出exports 和 module.exports 的区别
    Javascript modules--js 模块化
  • 原文地址:https://www.cnblogs.com/litiane/p/13736973.html
Copyright © 2020-2023  润新知