精华内容
下载资源
问答
  • 基于Vue+webuploader+element-ui的文件分片上传, 插件使用 npm i w-web-uploader --save 组件内使用 <VueWebUpload checkUrl="/storage/fdfs/checkFile" uploadUrl="/storage/fdfs/batchPeaceUpload" tip=...
  • 大文件上传vue+WebUploader

    千次阅读 2020-09-27 16:02:09
    大文件上传vue+WebUploader 说说我实习前端开发的时候用的大文件上传,前端原本项目用的是element自带的el-upload文件上传,确实很方便,element把数据上传成功,失败,上传中等等的监听事件都已经封装好了,文件...

    大文件上传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.等全部分片上传完成后向后端请求合并分块,成一个完整的文件。

    在这里插入图片描述html效果图

    前端代码示例

    下面贴上完整前端代码,自行修改请求路径,也可以修改文件的上传配置,可以修改样式,。。。
    可以在监听方法中写自己想要的功能代码。。。并且在上传中还包含了进度条信息可以看进度。。

    <template>
    	<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>
    </template>
    
    import apis from "@apis";
    import $ from "jquery";
    import WebUploader from "webuploader";
    
    export default {
      data() {
        return {
          projectContract: {},
        };
      },
    
      mounted() {
        $("#btnSync").hide();
        //该map,用于给uploader.options.formData表达赋一个动态的键值对.
        var map = {};
        // var testMd5;
        //定义文件的分片大小
        var chunkSize = 0.9 * 1024 * 1024;
        var _self = this;
    
        //监听分块上存过程中的三个时间点
        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();
              // setTimeout(() => {
              //   if (!map[file.id]) {
              //     // deferred.reject();
              //     alert("文件解析出错,请刷新后重新上传.");
              //     return deferred.promise();
              //   }
              // }, 1000);
    
              //显示暂停按键并且隐藏删除按键
              switchButton(file.id);
    
              //先查看服务器中是否已经有该文件
              $.ajax({
                type: "POST",
                dataType: "json",
                url: url_second_pass,//这是检查是否传过的请求路径
                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("span.state")
                      .text("已经上传");
                    // deferred.resolve();
                  } else {
                    $("#" + file.id)
                      .find("span.state")
                      .text("正在上传...");
                    deferred.resolve();
                  }
                },
                error: function (data) {
                  console.log("秒传错误");
                  deferred.reject();
                  $("#" + file.id)
                    .find("span.state")
                    .text("秒传出错...");
                  alert("网络错误,请刷新后再上传");
                },
              });
    
              return deferred.promise();
            },
            //时间点2:如果有分块上传,则每个分块上传之前调用此函数
            //用于文件的续传
            beforeSend: function (block) {
              console.log("执行时间点2的方法。。。");
              var deferred = WebUploader.Deferred();
              $.ajax({
                type: "POST",
                dataType: "json",
                url: url_check_chunk,//这里是检查文件分块的请求路径
                data: {
                  chunk: block.chunk,
                  //block.file.id,获取该分片对应的文件的id,从而获取该文件的md5值
                  md5Val: map[block.file.id],
                },
                success: function (data) {
                  console.log("成功切块", 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("span.state")
                .text("后台正在合并文件...");
              //上传成功后,异步请求后台的servlet,发送的数据有guid(该文件所有分片保存的目录),chunks(该文件一共分了多少片,注意要向上取整),filename(文件名)
              $.ajax({
                type: "POST",
                dataType: "json",
                url: apis.url_merge_chunk,//这里是合并文件的请求路径
                data: {
                  guid: uploader.options.formData.guid,
                  fileSize: file.size,
                  chunks: Math.ceil(file.size / chunkSize),
                  fileName: file.name,
                  md5Val: map[file.id],
                },
                success: (data) => {
                  if (data.status === "success") {
                    $("#" + file.id)
                      .find("span.state")
                      .text("已经上传");
                  } else {
                    $("#" + file.id)
                      .find("span.state")
                      .text("上传失败");
                  }
                },
                error: function (data) {
                  console.log("合并错误");
                  $("#" + file.id)
                    .find("span.state")
                    .text("合并文件出错...");
                },
              });
              console.log("执行时间点3的方法。。。");
            },
          }
        );
    
        var uploader = WebUploader.create({
          // swf文件路径
          swf: "webuploader/Uploader.swf",
          // 文件接收服务端。
          server: apis.url_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,
          //禁用全局拖拽功能
          disableGlobalDnd: true,
          // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
          resize: false,
        });
        // 当有文件被添加进队列的时候触发
        uploader.on("fileQueued", function (file) {
          // file.statusText = "未上传"
          console.log("添加", file);
    
          $("#thelist").append(
            '<div id="' +
              file.id +
              '" class="item">' +
              '<span class="info">' +
              file.name +
              "</span>" +
              '<span class="state">等待上传...</span>' +
              '<button class="btn btn-info btn-stop" style="display:none;">' +
              "暂停" +
              "</button>" +
              '<a id="del" href="javascript:void(0);" class="btn btn-primary file_btn btnRemoveFile" >' +
              "删除" +
              "</a>" +
              '<div class="progress">' +
              '<div id="' +
              file.id +
              'progress"  class="test-bar" style="width: 0%;" >' +
              "</div>" +
              "</div>" +
              "</div>"
          );
          new WebUploader.Uploader()
            .md5File(file, 0, 10 * 1024 * 1024)
            .progress(function (percentage) {
              $("#" + file.id)
                .find("span.state")
                .text("正在读取文件信息..." + parseInt(percentage * 100) + "%");
            })
            //当文件读取完后,就执行then方法
            .then(function (md5Val) {
              map[file.id] = md5Val;
              $("#" + file.id)
                .find("span.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");
    
            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;
            //渐变的效果的消失
            $(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 + "\n");
          //因为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", (file) => {
          switchButton(file.id);
          console.log("执行上传成功的方法。。");
          $("#" + file.id)
            .find("span.state")
            .text("上传成功。。。");
    
        });
    
        uploader.on("uploadError", function (file) {
    
          switchButton(file.id);
          $("#" + file.id)
            .find("span.state")
            .text("uploadError上传出错...");
        });
    
        //不管所有分片发送成功或者失败都会执行该方法
        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;
          }
          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");
          }
        }
      },
      methods: {
       
      },
    };
    </script>
    <style lang="scss" scope>
    </style>
    
    展开全文
  • 1、vue框架,package.json文件,dependencies依赖 "webuploader": "^0.1.8" 2、引用与注册 import WebUploader from 'webuploader' components: { WebUploader }, 3、页面渲染 <div id="uploader" ...

    1、vue框架,package.json文件,dependencies依赖

    "webuploader": "^0.1.8"

    2、引用与注册     

    import WebUploader from 'webuploader'
    
    
    components: {
        WebUploader
    },

    3、页面渲染

    <div id="uploader" class="wu-example">
         <div id="thelist1" 
            style="height:200px;overflow:auto;border:1px solid #C0C4CC;
            margin-bottom:10px;margin-top:14px;padding:0 15px;" 
            class="uploader-list"></div>
         <div class="btns">
              <div id="picker">选择文件</div>
         </div>
    </div>

    4、页面相关函数

    openUploadPro() {
        //初始化上传组件
        this.dialogFormVisible1 = true;
    },
    opendAfter() {//弹出层动画结束之后回调
        this.initWebUpload();
    },
    handleClose() {
        this.files = [];
        $("#thelist1").html('');
        this.uploader.reset();
    },
    //切换文件类型,清除其他类型的附件
    clearAppendix() {
        this.files = [];
        $("#thelist1").html('');
        this.uploader.reset();
    },
    initWebUpload() {
        let _this = this;
        var GUID = WebUploader.Base.guid();
        //确认按钮
        $("#btnUploadGW").click(function () {
            if (_this.files.length == 0) {
                stMsg("请选择要上传的文件", "warning");
                return;
            }
            _this.uploader.upload();
        })
    
        _this.uploader = WebUploader.create({
            // swf文件路径
            swf: '../../../static/JS/webupload/Uploader.swf',
            // 文件接收服务端。
            server: '../apis/service.do',
            // 选择文件的按钮。可选。
            // 内部根据当前运行是创建,可能是input元素,也可能是flash.
            pick: '#picker',
            // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
            resize: false,
            //开始分片上传
            chunked: true,
            //每一片的大小 2M
            chunkSize: 2097152,
            threads: 1,
            chunkRetry: 3,//网络问题上传失败后重试次数
            formData: {
                sc: "MaterialController",
                op: "UploadFileToFtpResTest",
                Parameter: loginT.Encrypt(JSON.stringify({
                    guid: GUID,
                }))
            }
        });
    
        // 当有文件被添加进队列的时候
        _this.uploader.on('fileQueued', function (file) {
            if (file.name.indexOf('#') != -1) {
                return;
            }
            else {
                $("#thelist1").append('<div id="' + file.id + '" class="item">' +
                '<label class="info">' + file.name + '</label>' +
                '<p class="state">等待上传...</p>' +
                '</div>');
                _this.files.push(file.name);
            }
        });
        // 文件上传过程中创建进度条实时显示。
        _this.uploader.on('uploadProgress', function (file, percentage) {
            var $li = $('#' + file.id),
                $percent = $li.find('.progress .progress-bar');
            // 避免重复创建
            if (!$percent.length) {
                $percent = $('<div class="progress progress-striped active">' +
                '<div class="progress-bar" role="progressbar" style="width: 0%">' +
                '</div>' +
                '</div>').appendTo($li).find('.progress-bar');
            }
            $li.find('p.state').text('上传中');
            $percent.css('width', percentage * 100 + '%');
        });
        //上传成功后
        _this.uploader.on('uploadSuccess', function (file, response) {
            if (response.success == false) {
            $('#' + file.id).find('p.state').text('上传出错');
                _this.uploader.stop(true);
                return;
            }
            //更新上传文件状态
            $('#' + file.id).find('p.state').text('已上传');
        });
        //上传错误
        _this.uploader.on('uploadError', function (file) {
            $('#' + file.id).find('p.state').text('上传出错');
        });
    
        _this.uploader.on('uploadFinished', function () {
            //清空文件数组
            _this.files.length = 0;
            //清空上传列表
            $("#thelist1").html('');
            stMsg("文件上传成功", "success");
            _this.uploader.reset();
            //关闭窗口刷新父页面
            _this.dialogFormVisible1 = false;
        });
        //上传完成
        _this.uploader.on('uploadComplete', function (file) {
            $('#' + file.id).find('.progress').fadeOut();
        })
    },
    guid() {
        return (
            this.S4() + this.S4() + "-" + 
            this.S4() + "-" + this.S4() + "-" + this.S4() + "-" + 
            this.S4() + this.S4() + this.S4());
    },
    S4() {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    },

    5、C# 接口

    //文件上传路径
    string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "pic");
    if (!Directory.Exists(path))
    {
        Directory.CreateDirectory(path);
    }
    //文件id、name、分片
    string id = context.Request.Form["id"];
    string filename = context.Request.Form["name"];
    int chunks = 0, chunk = 0;
    if (context.Request.Form["chunks"] != null)
    {
         chunks = int.Parse(context.Request.Form["chunks"]);
         chunk = int.Parse(context.Request.Form["chunk"]);
    }
    //文件后缀
    string ext = Path.GetExtension(filename);
    string savefile = DateTime.Now.ToFileTime() + ext;
    string url = savefile;
    
    HttpPostedFile file = context.Request.Files[0];
    if (chunks != 0)
    {
         string gurl = path + "/" + guid;
         string guidurl = gurl + "/" + id;
         if (!Directory.Exists(guidurl))
         {
              Directory.CreateDirectory(guidurl);
         }
         string f_index = chunk.ToString() + Path.GetExtension(filename);
         file.SaveAs(guidurl + "/" + f_index);
    
         if (chunk == chunks - 1)
         {
              //合并文件
              string targetPath = path + "/" + url;
              for (int i = 0; i < chunks; i++)
              {
                   using (FileStream addFile = new FileStream(targetPath, FileMode.Append, FileAccess.Write))
                   {
                        BinaryWriter bWriter = new BinaryWriter(addFile);
                        FileInfo fi = new FileInfo(guidurl + "/" + i + Path.GetExtension(filename));
                        Stream stream = fi.Open(FileMode.Open);
                        BinaryReader tempReader = new BinaryReader(stream);
                        bWriter.Write(tempReader.ReadBytes((int)stream.Length));
                        tempReader.Close();
                        stream.Close();
                   }
              }
         }
    }

    展开全文
  • 不再对使用问题进行答复,如果有希望的功能改进或者bugfix可以提交PR WebUploader 文件上传 WebUploader是一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。在现代的浏览器里面能充分发挥HTML5的优势,同时...
  • 1.下载webuploader 下载地址:官网http://fex.baidu.com/webuploader/ 2.引入webuploader ...这里我们使用完全压缩版的webuploader.min.js,同时还要引入Uploader.swf 、 webuploader.css,所以可以删除...

    1.下载webuploader

    下载地址:官网http://fex.baidu.com/webuploader/
    在这里插入图片描述

    2.引入webuploader

    使用Web Uploader文件上传需要引入三种资源:JS, CSS, SWF。
    方法1.直接引入文件
    这里我们使用完全压缩版的webuploader.min.js,同时还要引入Uploader.swf 、 webuploader.css,所以可以删除其他版本的文件,只留这三个文件,将解压的webuploader文件夹直接放在static文件夹下.

    在index.html中引入webuploader.min.js

    <script src="./static/webuploader-0.1.5/webuploader.min.js"></script>
    
    

    在这里插入图片描述
    重启项目后报错了,说缺少依赖jquery,打开源码webuploader.js看一下
    在这里插入图片描述
    原来webuploader是依赖于juqery或者Zepto的,那我们引入jquery,我们去jquery官网下载jquery,并在index.html中引入,注意,一定要在webuploader.min.js之前引入,在这里我下载的是1.12.4版本

    <script src="./static/webuploader-0.1.5/jQuery1.12.4.js"></script>
    

    方法2:模块化引入
    下载webuploder

    npm install webuploader --save
    

    下载jquery

    npm install jquery@1.12.4
    

    在App.vue中引入webuploder和jquery

    import $ from 'jquery'
    import WebUploader from 'webuploader'
    

    3.实例

    upload.vue

    <template>
        <div id="uploader" class="wu-example">
          <!--用来存放文件信息-->
          <div id="fileLilst" class="uploader-list"></div>
              <div class="btns">
                <!-- 选择文件的按钮 -->
                  <div id="picker">选择文件</div>
                <!-- 开始上传按钮 -->
                  <button id="uploadBtn" class="btn btn-default">开始上传</button>
          </div>
        </div>
    </template>
    <script>
    import $ from 'jquery'
    import WebUploader from 'webuploader'
    var fileMd5;
    //自定义参数 文件名
    var fileName, uploader;
    export default {
        name: 'upload',
    	mounted() {
    		this.getAllFileList()
          this.uploadPlan = `上传完成(${this.fileNum} / ${this.completeNum}`
    
          //监听分块上传过程中的三个时间点  
          WebUploader.Uploader.register({
            "before-send-file": "beforeSendFile",  //整个文件上传前调用
            "before-send": "beforeSend",  //每个分片上传前
            "after-send-file": "afterSendFile",  //分片上传完毕
          }, {
            //时间点1:所有分块进行上传之前调用此函数  
            beforeSendFile: function (file) {
              //console.log("beforeSendFile");
              var deferred = WebUploader.Deferred();
              //1、计算文件的唯一标记,用于断点续传  
              (new WebUploader.Uploader()).md5File(file, 0, 2 * 1024 * 1024)
                .progress(function (percentage) {
                  $('#item1').find("p.state").text("正在读取文件信息...");
                })
                .then(function (val) {
                  fileMd5 = val;
                  $('#item1').find("p.state").text("成功获取文件信息...");
                  //获取文件信息后进入下一步  
                  deferred.resolve();
                });
              return deferred.promise();
            },
            //时间点2:如果有分块上传,则每个分块上传之前调用此函数  
            beforeSend: function (block) {
              console.log("beforeSend111111");
              console.log(block.chunk);
              var deferred = WebUploader.Deferred();
              $.ajax({
                type: "POST",
                url: "http://58.251.218.38:8080/BIMService2/model/checkOrMerge?action=checkChunk",
                data: {
                  //文件唯一标记  
                  fileMd5: fileMd5,
                  //当前分块下标  
                  //chunk:block.chblocknk, 
                  chunk: block.chunk,
                  //当前分块大小  
                  chunkSize: block.end - block.start,
                  userId: localStorage.getItem("userId")
                },
                dataType: "json",
                success: function (response) {
                  if (eval('(' + response.ifExist + ')')) {
                    //console.log("分片存在:" + block.chunk)
                    //分块存在,跳过  
                    deferred.reject();
                  } else {
                    //console.log("分片不存在:" + block.chunk)
                    //分块不存在或不完整,重新发送该分块内容  
                    deferred.resolve();
                  }
                }
              });
              this.owner.options.formData.fileMd5 = fileMd5;
              console.log("继续执行")
              //deferred.resolve();  
              return deferred.promise();
            },
            //时间点3:所有分块上传成功后调用此函数  
            afterSendFile: function (file) {
              //console.log("afterSendFile");
              fileName = file.name; //为自定义参数文件名赋值
              //如果分块上传成功,则通知后台合并分块  
              $.ajax({
                type: "POST",
                url: "http://58.251.218.38:8080/BIMService2/model/checkOrMerge?action=mergeChunks",
                data: {
                  fileMd5: fileMd5,
                  fileName: fileName,
                  ext: file.ext, //文件扩展名
                  projectName: $("#project option:selected").text(), //项目名称
                  userId: localStorage.getItem("userId")
                },
                success: function (response) {
                  console.log(response)
                  //alert("上传成功");   
                }
              });
            }
          });
          uploader = WebUploader.create({
            swf: 'static/webuploader/Uploader.swf', // swf文件路径
            formData: { projectName: "", userId: localStorage.getItem("userId") },
            server: 'http://58.251.218.38:8080/BIMService2/model/upload', // 文件接收服务端。
            pick: '#picker', // 选择文件的按钮。可选
            chunked: true, //是否要分片处理大文件上传
            chunkSize: 2 * 1024 * 1024, //分片上传,每片5M,默认是5M
            auto: true, //选择文件后是否自动上传
            chunkRetry: 2, //如果某个分片由于网络问题出错,允许自动重传次数
            //runtimeOrder: 'html5,flash',
            // 在上传当前文件时,准备好下一个文件
            prepareNextFile: false,
            threads: 1, //threads {Boolean} [可选] [默认值:3] 上传并发数。允许同时最大上传进程数。
            duplicate: false,//是否重复上传(同时选择多个一样的文件),true可以重复上传  
            /* accept: {
              title: '语音上传',
              extensions: 'wav,zip,rar',
              mimeTypes: 'audio/x-wav,.zip,.rar'
            } */
          });
          this.initWebUpload()
    	},
    	methods: {
    		initWebUpload() {
    		//当文件被加入队列之前触发
                uploader.on('beforeFileQueued', (file, data) => {
                   //console.log('beforeFileQueued')
                        $.ajax({  
                            type:"POST",  
                            url:"http://58.251.218.38:8080/BIMService2/model/permissionVerification",  
                            data:{  
                                userId: localStorage.getItem("userId")
                            },  
                            dataType:"json",  
                            success (response){  
                                let msg = response.message
                                if (msg === '请先登录账号') {
                                    this.$message.error(msg);
                                    this.$router.push({path: '/Login'});
                                    return false;
                                } else if (msg === '您没有使用权限,请先联系管理员获取使用权限') {
                                    this.$message.error(msg);
                                }
                            }
                        }); 
                });
                // 当有文件被添加进队列的时候
                uploader.on('fileQueued', (file) => {
                    //console.log('fileQueued')
                    this.filesList.push({
                        id: file.id,
                        name: file.name,
                        size: this.reducedUnit(file.size),
                        percentage: 0
                    })
                    this.fileNum = this.filesList.length
                });
                //绑定uploadBeforeSend事件来给每个独立的文件添加参数
                uploader.on('uploadBeforeSend', (block, data) => {
                    //设置data参数
                    //data.projectName = $("#project").find("option:selected").text();   // 将存在file对象中的md5数据携带发送过去。
                }, 2);
                // 文件上传过程中创建进度条实时显示。
                uploader.on('uploadProgress', (file, percentage) => {
                    //console.log('uploadProgress')
                    this.filesList.forEach(item=>{
                        if(file.id===item.id) {
                            item.percentage = (percentage.toFixed(2))*100+"%"
                        }
                    })
                });
                // 文件上传成功
                uploader.on( 'uploadSuccess', (file, response)=> {
                    //console.log('uploadSuccess')
                    //console.log(response._raw); //这里可以得到后台返回的数据
                    this.filesList.forEach(item=>{
                        if(file.id===item.id) {
                            item.percentage = '上传成功'
                        }
                    })
                    this.completeNum++
                    this.getAllFileList()
                });
    
                // 文件上传失败,显示上传出错
                uploader.on('uploadError', (file, ret) => {
                    //console.log('上传失败')
                    this.filesList.forEach(item=>{
                        if(file.id===item.id) {
                            item.percentage = '上传失败'
                            }
                        })
                });
                // 完成上传完
                uploader.on('uploadComplete', (file) => {
                    $('#' + file.id).find('.progress').fadeOut();
                });
            },
            //暂停上传
            pauseUpload () {
                uploader.cancelFile(uploader.getFiles()[0].id);
            },
            //取消上传
            removeFile (index, row) {
                //this.filesList.splice(this.filesList.indexOf(row.name), 1);
                this.filesList.splice(index, 1)
            },
        }
    }
    

    4.遇到的问题

    1)WebUploader 插件的上传按钮点击无效,按F12 之后才有反应
    问题:上传按钮无效,debug的时候发现(上传id元素下面插件的渲染的宽度:1px,高度:1px),下面是错误图例说明:
    在这里插入图片描述
    解决方案:
    在页面加一段样式:

    <style>  
        #picker div:nth-child(2){width:100%!important;height:100%!important;}  
    </style> 
    

    如果上传按钮用的不是div 标签,改为div 标签,如<span id="picker">选择图片,改为<div id="picker">选择图片。

    如果做了第一步但用的是span 标签的话,只有鼠标在文字下方的按钮区域才有效(有效时,鼠标移上去按钮颜色会变深,无效时按钮没反应),按F12之后,只有鼠标在文字上方的按钮区域点击才有效;

    用div 的话就没这个问题,整个按钮都正常。
    2)修改按钮样式

    <style>
    .webuploader-container {
       position: relative;
    }
    .webuploader-element-invisible {
       position: absolute !important;
       clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
        clip: rect(1px,1px,1px,1px);
    }
    .webuploader-pick {
       position: relative;
       display: inline-block;
       cursor: pointer;
       background: #3376ff;
       padding: 8px 12px;
       color: #fff;
       font-size: 14px;
       text-align: center;
       border-radius: 3px;
       overflow: hidden;
    }
    .webuploader-pick-hover {
       background: #065ffc;
    }
    </style>
    

    参考网址:
    https://blog.csdn.net/Easy_____/article/details/78250736
    https://www.cnblogs.com/ocean-sky/p/7575980.html

    展开全文
  • 1,本项目基于vue-cli3,在public下创建webUploader目录 2,由于我司视频兼容能力业务需求,jQuery在index.html中引入。因此jQuery的引入方式有些简陋,多多包涵。。 3,文件分片断点续传底层能力组件 <...

    根于公司业务,封装了底层的 文件分片断点续传底层能力组件带有前端交互的UI组件

    GitHub

     

     

    使用步骤

    1,本项目基于vue-cli3,在public下创建webUploader目录

    2,由于我司视频兼容能力业务需求,jQuery在index.html中引入。因此jQuery的引入方式有些简陋,多多包涵。。

    3,文件分片断点续传底层能力组件

    大文件分片断点续传底层组件逻辑

    <template>
      <div class="upload" />
    </template>
    
    <script>
    import { fileUpload } from '@/api/upload-file'
    const qs = require('querystring')
    const md5 = window.md5
    const $ = window.$
    
    export default {
      name: 'VueWebuploader',
      props: {
        // 上传文件类型
        accept: {
          type: String,
          default: null
        },
        // 上传地址
        url: {
          required: true,
          type: String
        },
        // 上传文件总大小, 默认为100G
        fileSizeLimit: {
          type: Number,
          default: 100 * 1024 * 1024 * 1024
        },
        // 上传最大数量 默认为100个
        fileNumLimit: {
          type: Number,
          default: 100
        },
        // 大小限制 默认为100G
        fileSingleSizeLimit: {
          type: Number,
          default: 100 * 1024 * 1024 * 1024
        },
        // 上传时传给后端的参数,一般为token,key等
        formData: {
          required: true,
          type: Object
        },
        // 生成formData中文件的key,下面只是个例子,具体哪种形式和后端商议
        keyGenerator: {
          type: Function,
          default(file) {
            const currentTime = new Date().getTime()
            const key = `${currentTime}.${file.name}`
            return key
          }
        },
        multiple: {
          type: Boolean,
          default: false
        },
        // 上传按钮{Seletor|dom}
        uploadButton: {
          required: true,
          type: String
        },
        // 拖拽的容器{Selector}
        uploadDndButton: {
          type: String,
          default: ''
        }
      },
      data() {
        return {
          uploader: null,
          publicOption: {},
          uploadContCommon: {
            onUploadFileErr: null,
            onUploadFileSuccess: null,
            callbackDate: null
          },
          chunkSize: 5 * 1024 * 1024, // 分块大小
          fileMd5MarkMap: new Map(),
          uniqueFileNameMap: new Map()
        }
      },
      mounted() {
        this.initWebUpload()
      },
      methods: {
        initWebUpload() {
          const that = this
          new WebUploader.Uploader.register(
            {
              'before-send-file': 'beforeSendFile',
              'before-send': 'beforeSend',
              'after-send-file': 'afterSendFile'
            },
            {
              // // 上传文件开始前触发MD5校验,用了这个会很慢很慢,因此注掉
              // beforeSendFile: function(file) {
              //   if (that.publicOption.uploadProgress) {
              //     that.publicOption.beforeSendFile(file)
              //   }
              //   console.log('上传文件前触发')
              //   // return false
              //   // 秒传验证
              //   // const task = new $.Deferred()
              //   that.uploader.md5File(file).progress(percentage => {
              //     // 及时显示进度
              //     console.log('percentage', percentage)
              //   }).then(function(val) {
              //     // 完成
              //     console.log('上传文件前触发完成', val)
              //     // $('#' + file.id + ' .operate .del_btn').show()
              //     // $("#" + file.id + " .operate .pause_btn").show();
              //     // console.log("总耗时: "+ ((new Date().getTime()) - start)/ 1E3);
              //     // md5Mark = val
              //     that.fileMd5MarkMap.set(file.id, val)
              //     fileUpload(qs.stringify({ status: 'md5Check', md5: val }), that.url).then(res => {
              //       const { data } = res
              //       if (data.ifExist) {
              //         that.uploader.skipFile(file)
              //         file.path = res.path
              //         that.UploadComlate(file, this.fileMd5MarkMap.get(file.id))
              //       }
              //     })
              //   })
              // },
              // 开始上传分片触发
              beforeSend: function(block) {
                // 分片验证是否已传过,用于断点续传
                var task = new $.Deferred()
                var file = block.file
                var id = file.id
    
                that.uploader.md5File(file, block.start, block.end).then(val => {
                  var params = {
                    status: 'chunkCheck',
                    name: that.uniqueFileNameMap.get(id),
                    chunkIndex: block.chunk,
                    size: block.end - block.start,
                    userId: that.formData.userId,
                    companyId: that.formData.companyId,
                    appCode: that.formData.appCode,
                    blockmd5: val
                  }
    
                  fileUpload(qs.stringify(params), that.url).then(res => {
                    const { data } = res
                    if (data.ifExist === 1) { // 若存在,返回失败给WebUploader,表明该分块不需要上传
                      task.reject()
                    } else {
                      task.resolve()
                    }
                  }, function(jqXHR, textStatus, errorThrown) { // 任何形式的验证失败,都触发重新上传
                    task.reject()
                  })
                })
                return $.when(task)
              },
              // 上传文件完成触发
              afterSendFile: function(file) {
                var chunksTotal = Math.ceil(file.size / that.chunkSize)
                // 合并请求
                var task = new $.Deferred()
                var params = {
                  status: 'chunksMerge',
                  name: that.uniqueFileNameMap.get(file.id),
                  chunks: chunksTotal,
                  ext: file.ext,
                  md5: that.fileMd5MarkMap.get(file.id),
                  userId: that.formData.userId,
                  companyId: that.formData.companyId,
                  appCode: that.formData.appCode
                }
    
                fileUpload(qs.stringify(params), that.url).then(res => {
                  const { data } = res
                  if (data.path) {
                    file.path = data.path
                    // 最初的时候是在uploadSuccess发布的成功事件,后来发现有时取不到返回的file.path,因此换到了在merge文件碎片回调中发布上传成功事件
                    that.$emit('success', file, res)
                  }
                  task.resolve()
                  that.uniqueFileNameMap.delete(file.id)
                  that.fileMd5MarkMap.delete(file.id)
                }).catch(err => {
                  console.info(err)
                })
                return $.when(task)
              }
            })
    
          this.uploader = WebUploader.create({
            auto: true, // {Boolean} [可选] [默认值:false] 设置为 true 后,不需要手动调用上传,有文件选择即开始上传(选完文件后,是否自动上传)
            swf: '/public/webUploader/js/Uploader.swf', // swf文件路径
            server: this.url + 'fileUpload', // 文件接收服务端
            pick: { // 指定选择文件的按钮容器,不指定则不创建按钮
              id: this.uploadButton, // {Seletor|dom} 指定选择文件的按钮容器,不指定则不创建按钮。注意 这里虽然写的是 id, 但是不是只支持 id, 还支持 class, 或者 dom 节点。
              multiple: this.multiple, // {Boolean} 是否开启同时选择多个文件能力。
              label: '', // {String} 请采用 innerHTML 代替
              innerHTML: '' // {String} 指定按钮文字。不指定时优先从指定的容器中看是否自带文字
            },
            dnd: this.uploadDndButton, // {Selector} [可选] [默认值:undefined] 指定Drag And Drop拖拽的容器,如果不指定,则不启动。
            disableWidgets: 'log', // {String, Array} [可选] [默认值:undefined] 默认所有 Uploader.register 了的 widget 都会被加载,如果禁用某一部分,请通过此 option 指定黑名单。
            paste: document.body, // {Selector} [可选] [默认值:undefined] 指定监听paste事件的容器,如果不指定,不启用此功能。此功能为通过粘贴来添加截屏的图片。建议设置为document.body.
            disableGlobalDnd: true, // {Selector} [可选] [默认值:false] 是否禁掉整个页面的拖拽功能,如果不禁用,图片拖进来的时候会默认被浏览器打开。
            thumb: { // {Object} [可选] 配置生成缩略图的选项。
              width: 100,
              height: 100,
              quality: 100, // // 图片质量,只有type为`image/jpeg`的时候才有效。
              allowMagnify: false, // // 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false.
              crop: true // 是否允许裁剪。
              // type: 'image/jpeg' // 为空的话则保留原有图片格式,否则强制转换成指定的类型。
            },
            compress: false, // {Object} [可选] 配置压缩的图片的选项。如果此选项为false, 则图片在上传前不进行压缩。
            prepareNextFile: true, // {Boolean} [可选] [默认值:false] 是否允许在文件传输时提前把下一个文件准备好。 对于一个文件的准备工作比较耗时,比如图片压缩,md5序列化。 如果能提前在当前文件传输期处理,可以节省总体耗时。
            chunked: true, // {Boolean} [可选] [默认值:false] 是否要分片处理大文件上传。
            chunkSize: this.chunkSize, // {Boolean} [可选] [默认值:5242880] 如果要分片,分多大一片? 默认大小为5M.
            threads: 5, // {Boolean} [可选] [默认值:3] 上传并发数。允许同时最大上传进程数。
            formData: this.formData, // {Object} [可选] [默认值:{}] 文件上传请求的参数表,每次发送都会发送此对象中的参数。
            fileSizeLimit: this.fileSizeLimit, // {int} [可选] [默认值:undefined] 验证文件总大小是否超出限制, 超出则不允许加入队列。
            fileSingleSizeLimit: this.fileSingleSizeLimit, // {int} [可选] [默认值:undefined] 验证单个文件大小是否超出限制, 超出则不允许加入队列。
            fileNumLimit: this.fileNumLimit, // {int} [可选] [默认值:undefined] 限制上传个数,验证文件总数量, 超出则不允许加入队列。
            resize: false, // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
            accept: this.getAccept(this.accept), // {Arroy} [可选] [默认值:null] 指定接受哪些类型的文件。 由于目前还有ext转mimeType表,所以这里需要分开指定。
            duplicate: true // {Boolean} [可选] [默认值:undefined] 去重, 根据文件名字、文件大小和最后修改时间来生成hash Key.
          })
    
          // 当有文件被添加进队列的时候,添加到页面预览
          this.uploader.on('fileQueued', (file) => {
            const that = this
            // 将事件绑定到文件,保证在发生页面切换时之前已经开始上传的文件的回调事件不会发生变化
            file.onUploadFileErr = that.uploadContCommon.onUploadFileErr
            file.onUploadFileSuccess = that.uploadContCommon.onUploadFileSuccess
            file.callbackDate = that.uploadContCommon.callbackDate
    
            that.uploader.sort(function(a, b) { return a.size - b.size })// 多文件同时加入时小文件优先上传
            var uniqueFileName = md5(file.name + file.size + file.type + that.formData.userId)// 文件唯一标识
            that.uniqueFileNameMap.set(file.id, uniqueFileName)
            that.uploader.makeThumb(file, function(error, ret) {
              if (error) {
                // console.log('预览错误,上传的不是图片吧?')
              } else {
                // console.log('预览成功 base64')
              }
            })
            that.$emit('fileChange', file)
          })
    
          this.uploader.on('uploadStart', (file) => {
            // 在这里可以准备好formData的数据
            // this.uploader.options.formData.key = this.keyGenerator(file);
          })
    
          // 文件上传过程中创建进度条实时显示。
          this.uploader.on('uploadProgress', (file, percentage) => {
            this.$emit('progress', file, percentage)
          })
    
          this.uploader.on('uploadSuccess', (file, response) => {
            // this.$emit('success', file, response)
            // 最初的时候是在此处发布的成功事件,后来发现有时取不到返回的file.path,因此换到了在merge文件碎片回调中发布上传成功事件
          })
    
          this.uploader.on('uploadError', (file, reason) => {
            this.$emit('uploadError', file, reason)
          })
    
          this.uploader.on('error', (type) => {
            let errorMessage = ''
            if (type === 'F_EXCEED_SIZE') {
              errorMessage = `文件大小不能超过${this.fileSingleSizeLimit / (1024 * 1000)}M`
            } else if (type === 'Q_EXCEED_NUM_LIMIT') {
              errorMessage = '文件上传已达到最大上限数'
            } else {
              errorMessage = `上传出错!请检查后重新上传!错误代码${type}`
            }
            this.$emit('error', errorMessage)
          })
    
          this.uploader.on('uploadComplete', (file, response) => {
            this.$emit('complete', file, response)
          })
        },
    
        upload(file) {
          this.uploader.upload(file)
        },
        stop(file) {
          this.uploader.stop(file)
        },
        // 取消并中断文件上传
        cancelFile(file) {
          this.uploader.cancelFile(file)
        },
        // 在队列中移除文件
        removeFile(file, bool) {
          this.uploader.removeFile(file, bool)
        },
    
        // 获取上传文件类型
        getAccept(accept) {
          switch (accept) {
            case 'text':
              return {
                title: 'Texts',
                exteensions: 'doc,docx,xls,xlsx,ppt,pptx,pdf,txt', // {String} 允许的文件后缀,不带点,多个用逗号分割。
                mimeTypes: '.doc,docx,.xls,.xlsx,.ppt,.pptx,.pdf,.txt' // {String} 多个用逗号分割。
              }
            case 'video':
              return {
                title: 'Videos',
                exteensions: 'mp4',
                mimeTypes: '.mp4'
              }
            case 'image':
              return {
                title: 'Images',
                exteensions: 'gif,jpg,jpeg,bmp,png',
                mimeTypes: '.gif,.jpg,.jpeg,.bmp,.png'
              }
            default: return accept
          }
        }
      }
    }
    </script>
    
    <style lang="scss">
      .webuploader-container {
        position: relative;
      }
      .webuploader-element-invisible {
        position: absolute !important;
        clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
        clip: rect(1px,1px,1px,1px);
      }
      .webuploader-pick {
        position: relative;
        display: inline-block;
        cursor: pointer;
        background: #00b7ee;
        padding: 10px 15px;
        text-align: center;
        border-radius: 3px;
        overflow: hidden;
      }
      .webuploader-pick-hover {
        background: #00a2d4;
      }
    
      .webuploader-pick-disable {
        opacity: 0.6;
        pointer-events:none;
      }
    </style>
    

    4,文件分片断点续传UI组件

    <!--/**-->
    <!--* @Author: YanHuaKang-->
    <!--* @Date: 2019/11/21-->
    <!--* @Description: 文件分片断点续传UI组件-->
    <!--* @remarks:-->
    <!--*/-->
    <template>
      <transition :name="transitionName">
        <div id="mainFileUpload" :style="visibility">
          <div id="header" class="clearfix">
            <p class="fl title">上传资料</p>
            <div class="fr icons">
              <span
                v-if="!isMinimality"
                @click="minimality"
              ><i
                class="minimality iconfont iconjianhao"
              /></span>
              <span
                v-if="isMinimality"
                @click="open"
              ><i
                class="spread iconfont iconjiahao"
              /></span>
              <span @click="close"><i class="close iconfont iconguanbi" /></span>
            </div>
          </div>
          <div class="content">
            <div id="filePicker">
              <div class="drag-upload">
                <i class="iconfont iconshangchuan" />
                <p>可拖拽文件至此直接上传</p>
              </div>
            </div>
            <div id="file-panel">
              <div id="file-list">
                <el-scrollbar wrap-class="scrollbar-wrapper">
                  <ul
                    v-for="(file, index) in fileList"
                    :key="file.id"
                    class="file-item"
                    :class="`file-${file.id}`"
                  >
                    <li class="file-type" :icon="fileCategory(file.ext)">
                      <svg v-if="fileCategory(file.ext) === 'image'" class="icon" aria-hidden="true">
                        <use xlink:href="#icontupian" />
                      </svg>
                      <svg v-else-if="fileCategory(file.ext) === 'video'" class="icon" aria-hidden="true">
                        <use xlink:href="#iconvideo-" />
                      </svg>
                      <svg v-else-if="fileCategory(file.ext) === 'audio'" class="icon" aria-hidden="true">
                        <use xlink:href="#iconaudio" />
                      </svg>
                      <svg v-else-if="fileCategory(file.ext) === 'text'" class="icon" aria-hidden="true">
                        <use xlink:href="#icontext" />
                      </svg>
                      <svg v-else-if="fileCategory(file.ext) === 'excel'" class="icon" aria-hidden="true">
                        <use xlink:href="#iconex" />
                      </svg>
                      <svg v-else-if="fileCategory(file.ext) === 'ppt'" class="icon" aria-hidden="true">
                        <use xlink:href="#iconppt" />
                      </svg>
                      <svg v-else-if="fileCategory(file.ext) === 'pdf'" class="icon" aria-hidden="true">
                        <use xlink:href="#iconpdf1" />
                      </svg>
                      <svg v-else-if="fileCategory(file.ext) === 'compressed'" class="icon" aria-hidden="true">
                        <use xlink:href="#iconyasuobao" />
                      </svg>
                      <svg v-else class="icon" aria-hidden="true">
                        <use xlink:href="#iconyemian" />
                      </svg>
                    </li>
                    <el-tooltip
                      class="file-name-tip"
                      effect="dark"
                      :content="file.name"
                      placement="top-start"
                    >
                      <li class="file-name singleLineOmission">{{ file.name }}</li>
                    </el-tooltip>
                    <li class="progress">
                      <el-progress :key="index" :percentage="progress[file.id]" :color="customColors" />
                    </li>
                    <li class="file-size">{{ fileSize(file.size) }}</li>
                    <!--<li class="file-status">上传中...</li>-->
                    <li class="file-operate">
                      <!--<a title="开始" @click="resume(file)"><i class="iconfont iconkaishi" /></a>-->
                      <!--<a title="暂停" @click="stop(file)"><i class="iconfont iconzanting1" /></a>-->
                      <span
                        class="pointer file-remove"
                        title="移除"
                        @click="remove(file)"
                      ><i
                        class=" iconfont iconguanbi"
                      /></span>
                    </li>
                  </ul>
                  <div v-if="!fileList.length" class="no-file">
                    <i class="iconfont icon-empty-file" /> 暂无待上传文件
                  </div>
                </el-scrollbar>
              </div>
            </div>
          </div>
          <div class="clearfix btnGroup">
            <div id="btnGroup" class="fr">
              <el-button type="primary" @click="clear()">清空上传</el-button>
            </div>
          </div>
          <vue-webuploader
            ref="uploader"
            upload-button="#filePicker"
            upload-dnd-button="#filePicker"
            multiple
            :form-data="formData"
            :url="url"
            @fileChange="fileChange"
            @progress="onProgress"
            @success="onSuccess"
          />
        </div>
      </transition>
    </template>
    
    <script>
    import store from '@/store'
    import { mapGetters } from 'vuex'
    import VueWebuploader from '@/components/VueWebuploader/VueWebuploader.vue'
    import { deskAddFile, knowledgeCreateFile } from '@/api/upload-file'
    const WebUploader = window.WebUploader
    export default {
      name: 'FileUploader',
      components: {
        VueWebuploader
      },
      props: {
        customStyle: {
          type: Object,
          default: function() {
            return {
              right: '50px',
              bottom: '0px'
            }
          }
        },
        transitionName: {
          type: String,
          default: 'fade'
        }
      },
      data() {
        return {
          uploadButton: 'filePicker',
          fileList: [], // 上传列表
          url: this.$store.state.user.applicationInfo.uploadUrl, // 租户上传路径
          formData: {
            // 参数
            userId: this.$store.state.user.applicationInfo.userId,
            companyId: this.$store.state.user.applicationInfo.companyId,
            appCode: this.$store.state.user.applicationInfo.appCode
          },
          fileQueued: new Map(), // 上传队列
          customColors: [
            { color: '#f56c6c', percentage: 20 },
            { color: '#e6a23c', percentage: 40 },
            { color: '#5cb87a', percentage: 60 },
            { color: '#1989fa', percentage: 80 },
            { color: '#20c7b3', percentage: 100 }
          ],
          progress: {} // 进度
        }
      },
      computed: {
        ...mapGetters(['visibility', 'isMinimality', 'belongs'])
      },
      watch: {},
      mounted() {
        store.dispatch('fileUpload/isVisibility', 2)
      },
      created() {},
      methods: {
        // 关闭
        close() {
          store.dispatch('fileUpload/isVisibility', 2)
        },
        // 最小化
        minimality() {
          store.dispatch('fileUpload/isVisibility', 3)
        },
        // 展开
        open() {
          store.dispatch('fileUpload/isVisibility', 1)
        },
    
        // 上传变化
        fileChange(file) {
          if (!file.size) return
          file.progress = 10
          this.fileList.push(file)
          this.$set(this.progress, file.id, 0)
          const sourceUid = file.source.uid // 文件上传文件唯一id
          const belongs = {
            data_type: this.belongs.data_type,
            ownerId: this.belongs.ownerId,
            parentId: this.belongs.parentId,
            selectCompanyId: this.belongs.selectCompanyId
          }
          this.fileQueued.set(sourceUid, belongs)
        },
    
        // 上传进度
        onProgress(file, percent) {
          const progress = (Math.ceil(percent * 100 * 100) / 100)
          this.$set(this.progress, file.id, progress)
        },
    
        // 上传成功
        onSuccess(file, response) {
          // console.log('file', file)
          // console.log('response', response)
          // console.log('上传成功', file)
          // console.log(this.belongs)
          // console.log('file.path', file.path)
          file.path = file.path || ''
          const fileId = file.path.split('ZmlsZUlk=')[1]
          const sourceUid = file.source.uid // 文件上传文件唯一id
          var currentFile = this.fileQueued.get(sourceUid)
          // console.log('sourceUid', sourceUid)
          // console.log('currentFile', currentFile)
          // console.log('this.fileQueued', this.fileQueued)
          // console.log('fileId', fileId)
          if (currentFile.data_type === 3) {
            const params = {
              fileFormat: file.ext,
              fileName: file.name,
              fileSize: file.size,
              fileUrl: file.path,
              resourceFileId: fileId
            }
            deskAddFile(params).then(res => {
              console.log(res)
              this.$message.success('上传成功!')
              store.dispatch('fileUpload/deskAddFileSuccess', res.data)
            })
          } else {
            // const currentFile = this.fileQueued.get(sourceUid)
            const params = {
              format: file.ext,
              name: file.name,
              size: file.size,
              url: file.path,
              data_type: currentFile.data_type,
              ownerId: currentFile.ownerId,
              parentId: currentFile.parentId,
              selectCompanyId: currentFile.selectCompanyId,
              fileId: fileId
            }
            knowledgeCreateFile(params).then(res => {
              // console.log(res)
              this.$message.success('上传成功!')
              store.dispatch('fileUpload/createFileSuccess', res.data)
            })
          }
        },
    
        // 开始
        resume(file) {
          this.$refs.uploader.upload(file)
        },
        // 暂停
        stop(file) {
          this.$refs.uploader.stop(file)
        },
    
        // 移除
        remove(file) {
          this.$confirm('确定要删除此上传任务吗?', '删除任务', {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
          })
            .then(() => {
              // 取消并中断文件上传
              this.$refs.uploader.cancelFile(file)
              // 在队列中移除文件
              this.$refs.uploader.removeFile(file, true)
    
              // 在ui上移除
              const index = this.fileList.findIndex(ele => ele.id === file.id)
              this.fileList.splice(index, 1)
            })
            .catch(() => {})
        },
    
        // 清空
        clear() {
          var that = this
          if (this.fileList.length) {
            this.$confirm('确定要清空上传任务吗?', '清空上传', {
              confirmButtonText: '确定',
              cancelButtonText: '取消',
              type: 'warning'
            })
              .then(() => {
                for (
                  var index = 0, len = that.fileList.length;
                  index < len;
                  index++
                ) {
                  var file = that.fileList[index]
                  that.$refs.uploader.cancelFile(file)
                  that.$refs.uploader.removeFile(file, true)
                  that.fileList.splice(index--, 1)
                }
              })
              .catch(() => {})
          }
        },
    
        // 文件大小
        fileSize(size) {
          return WebUploader.Base.formatSize(size)
        },
    
        // 文件类别
        fileCategory(ext) {
          let type = ''
    
          const typeMap = {
            image: ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp'],
            video: ['mp4', 'm3u8', 'rmvb', 'avi', 'swf', '3gp', 'mkv', 'flv'],
            audio: ['mp3', 'wma', 'rm', 'wav', 'midi', 'ape', 'flac'],
            text: [
              'doc',
              'txt',
              'docx',
              'pages',
              'epub'
            ],
            excel: ['numbers', 'csv', 'xls', 'xlsx'],
            ppt: ['keynote', 'ppt', 'pptx'],
            compressed: ['zip', 'rar'],
            pdf: ['pdf']
          }
    
          Object.keys(typeMap).forEach(_type => {
            const extensions = typeMap[_type]
            if (extensions.indexOf(ext) > -1) {
              type = _type
            }
          })
    
          return type
        }
      }
    }
    </script>
    
    <style lang="scss" scoped>
    @import "~@/styles/theme.scss";
    
    #mainFileUpload {
      visibility: hidden;
      width: 38%;
      height: 60vh;
      position: fixed;
      bottom: -522px;
      right: 20px;
      z-index: 100;
      border: 1px solid #e0e0e0;
      border-radius: 5px;
      background: #ffffff;
      overflow: hidden;
      transition: bottom 1s;
    
      > #header {
        height: 40px;
        line-height: 40px;
        padding: 0 20px;
        background-color: $themeColor;
    
        > .title {
          color: #ffffff;
          margin: 0;
          font-size: 14px;
        }
    
        > .fr.icons span {
          width: 14px;
          height: 14px;
          background-color: #ffffff;
          color: $themeColor;
          cursor: pointer;
        }
    
        > .fr.icons span i {
          font-size: 14px;
        }
      }
    
      > .content {
        padding: 1.5vh 20px;
    
        > #filePicker {
          width: 100%;
          height: 15vh;
          background-color: #fafafa;
          border-radius: 5px;
        }
      }
    
      .drag-upload {
        width: 200px;
        margin: 0 auto;
        margin-top: 18px;
        color: #bbbbbb;
    
        > .iconfont {
          font-size: 50px;
        }
      }
    
      #file-panel {
        margin-top: 1.5vh;
      }
    
      #file-panel .el-scrollbar {
        height: 29vh;
      }
    
      #file-list {
        position: relative;
        overflow-y: auto;
      }
    
      .file-item {
        position: relative;
        height: 30px;
        line-height: 30px;
        padding: 0 10px;
        margin-bottom: 10px;
        border-bottom: 1px solid #ccc;
        background-color: #fff;
        z-index: 1;
        color: #575757;
    
        > li {
          display: inline-block;
        }
    
        > .file-type {
          width: 24px;
          height: 30px;
          vertical-align: top;
    
          > i {
            display: table-cell;
          }
        }
        > .file-name {
          width: 18%;
          vertical-align: top;
        }
        > .progress {
          width: 70%;
    
          > /deep/ .el-progress {
            width: 100%;
            height: 30px;
            line-height: 30px;
            display: table;
          }
        }
    
        > .file-size {
          width: 80px;
          position: absolute;
          left: 0px;
          right: 0px;
          bottom: 0px;
          top: 0px;
          margin: 0 auto;
          font-size: 14px;
          text-align: center;
        }
      }
    
      .file-remove {
        display: table-cell;
      }
    
      .no-file {
        text-align: center;
        color: #575757;
        font-size: 14px;
      }
    }
    .btnGroup {
      margin-right: 20px;
    }
    
    .file-item .progress /deep/ .el-progress-bar {
      width: 95%;
    }
    
    #filePicker /deep/ .webuploader-pick {
      width: 100% !important;
      height: 100% !important;
      background-color: #fafafa !important;
    }
    
    #filePicker /deep/ .webuploader-pick-hover {
      background-color: #fafafa !important;
    }
    #mainFileUpload > #header > .fr.icons span {
      padding: 0 1px;
      border-radius: 2px;
    }
    </style>
    

    5,效果图

    展开全文
  • WebUploader是由 Baidu WebFE(FEX) 团队开发的一个简单的以 HTML5为主 , FLASH为辅 的现代 文件上传组件 。这篇文章主要介绍了在Vue项目中使用WebUploader实现文件上传,需要的朋友可以参考下
  • Vue中将使用WebUploader实现的上传功能封装成一个单独的上传组件,但是在多个页面使用上传组件的时候,在页面之前跳转(非刷新)会导致多次注册WebUploader,最终出现一次上传触发多个上传操作。 解决方法: 在...
  • 本篇文章主要介绍了vue webuploader 文件上传组件开发,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • VueWebuploader.zip

    2021-09-08 19:09:04
    大文件分配断点续传
  • WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。在现代的浏览器里面能充分发挥HTML5的优势,同时又不摒弃主流IE浏览器,沿用原来的FLASH运行时,兼容IE6+,iOS 6+,...
  • vue WebUploader 分块上传

    2020-06-01 13:58:32
    由于项目使用的是BJUI前端框架,并没有使用框架本身的文件上传控件,而使用的基于jQuery的Uploadify文件上传组件,在项目使用的jslib项目中找到了BJUI框架集成jQuery Uploadify的部分,这部分代码封装在bjui-all.js...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 830
精华内容 332
关键字:

vue使用webuploader

vue 订阅