• 企业网盘之分片上传组件


    效果展示

     包含技术点

    1、分片上传。

    2、文件秒传。

    3、文件夹上传

    4、文件续传。

    5、文件拖拽上传。

    组件目录

     

    实现分析

    分片上传

    通过H5 FileUpload对象可以实现文件上传, mutiple属性可以支持文件多选。拿到文件对象后,调用完整的分片上传流程:计算MD5-添加文件-获取鉴权信息-递归上传分片-上传完成。

    如何切割文件分片?

    方法介绍:blob.slice(); 属于Blob对象的一个方法,而File对象是继承Blob对象的,因此File对象也含有slice方法。

    如何计算进度?
    通过文件大小及分片计算算法,算出分片大小和分片数目以后,通过当前上传的分片数目除以总共分片数目得到进度信息。

    如何计算上传速度?

    每上传一个分片,记录分片上传请求的时间,通过分片的大小除以请求时间得到速度信息。

    上传分片的代码:

    //  上传分片
      const uploadPart = (resolve, reject) => {
        let blob = file.slice((params.PartNumber - 1) * chunkSize, params.PartNumber * chunkSize);
        let begin = new Date().getTime();
        params['Body'] = blob;
        s3.uploadPart(params, function (err, data) {
          if (err || item.status !== 'uploading') {
            reject(err || item.status);
          } else {
            item.percentage = Math.round(params.PartNumber * 100 / frameNum); // 进度
            let spend = (new Date().getTime() - begin) / 1000;  // 消耗时间
            item.speed = Math.round(blob.size / spend) * 1.3;  // 速度
            log_content.frameSeq = params.PartNumber;
            l_params.logContent = JSON.stringify(log_content);
            // debugger;
            log(l_params).then(() => {
              if (params.PartNumber < frameNum) {
                params.PartNumber += 1;
                uploadPart(resolve, reject);
              } else {
                item.status = 'success';
                resolve();
              }
            }).catch((err) => {
              reject(err);
            });
          }
        });
      };

    文件秒传

    秒传是将上传的文件与服务器中的文件进行比对,若云端存在相同文件,则将直接把文件秒速保存到你的网盘。调用“添加文件”接口,若服务端返回文件ID,表明云端已存储相同文件,新的文件会存储为源文件的一份索引。

    文件夹上传

    chrome的私有属性webkitdiretory可以支持文件夹上传。分片上传流程需要增加递归创建文件夹:递归创建文件夹-计算MD5-添加文件-获取鉴权信息-递归上传分片-上传完成。

    判断文件为文件夹上传?

    file.webkitRelativePath属性。

    判断文件是否一次上传?

    这里要处理的场景是,一个文件夹上传两次,第二次需要对文件夹重命名。如何判断一批文件是一次上传的,还是同名文件夹上传多次。可以通过上传框change的时候,构造文件对象增加时间戳属性,同一批文件的时间戳相同。

    HTML:

    <input ref="folder" @change='handleChange' name="folderInput" multiple="" webkitdirectory="" accept="*/*" type="file">

    递归创建文件夹可参考Node算法:

    const mkdirs = (dirname, callback, errback) => {
      fs.stat(dirname, (err, stats) => {
        if (err) {
          mkdirs(path.dirname(dirname), () => {
            fs.mkdir(dirname, callback)
          }, errback)
        } else {
            if (stats.isDirectory()) {
              callback()
            } else {
              errback()
            }
        }
      })
    }

    文件续传

    大文件在上传过程中进行中断网络或刷新浏览器等操作,重新登录可断点上传。后台记录文件上传的分片信息,当上传过程被终止以后,重新登录查询当前用户未上传成功的续传列表。存储那边会保留已上传的分片,断点上传从未上传的分片开始,可大大减少大文件上传终止需重新上传的时间。

    文件拖拽上传

    H5新特性可实现文件拖拽上传。其中,与拖拽文件相关的事件有dragover(文件拖拽在悬浮)dragleave(文件拖拽离开)drop(文件拖拽放下)。在事件对象中,一个e.dataTransfer这样的属性,它是一个DataTransfer类型的数据,有如下的属性

    属性 类型 说明
    files FileList 拖拽的文件列表
    items DataTransferItemList 拖拽的数据(有可能是字符串)
    types Array 拖拽的数据类型 该属性在Safari下比较混乱

    完整的组件代码:

    <template>
      <div
        class="uploadMask"
        style="position: fixed"
        :class="{
          'is-dragover': dragover
        }"
        v-show="dragover"
        @drop.prevent="onDrop"
        @dragover.prevent="onDragover"
        @dragleave.prevent="dragover = false"
      >
        <slot></slot>
      </div>
    </template>
    <script>
      export default {
        name: 'ElUploadDrag',
        props: {
          disabled: Boolean
        },
        data() {
          return {
            dragover: false
          };
        },
        methods: {
          onDragover() {
            if (!this.disabled) {
              this.dragover = true;
            }
          },
          /*eslint-disable*/
          onDrop(e) {
            let _this = this;
            if (!this.disabled) {
              let event = e || window.event;
              this.dragover = false;
              let df = event.dataTransfer;  // 拖曳操作的过程中,我们可以用过dataTransfer对象来传输数据,以便在拖曳操作结束的时候对数据进行其他的操作;
              let len = df.files.length;
              let dealFileCnt = 0; // 读取文件是个异步的过程,需要记录处理了多少个文件了
              let files = [];
    
              function callback (files) { // 抛出文件数组
                _this.$emit('file', files);
              }
    
              // 检测是否已经把所有的文件都遍历过了
              function checkDropFinish () {
                if ( dealFileCnt === len - 1 ) {
                  console.log('ie');
                  callback(files); //  所有的文件都遍历过了emit 出去
                }
                dealFileCnt++;
              }
    
              if (df.items) {  // 有dataTransfer项目列表时
                for (let i = 0; i < len; i++) {
                  let entry = df.items[i].webkitGetAsEntry(); // 读取拖拽元素信息
                  if (entry.isFile && !entry.isDirectory) {  // isDirectory是否是文件夹
                    files.push(df.files[i]);
                  }
                }
                callback(files);
              } else {  // ie浏览器
                for (let i = 0; i < len; i++) {
                  let dropFile = df.files[i];
                  if (dropFile.type) {
                    files.push(dropFile);
                    checkDropFinish()
                  } else {
                    try {
                      var fileReader = new FileReader();
                      fileReader.readAsDataURL(dropFile.slice(0, 3));
    
                      fileReader.addEventListener('load', function (e) {
                        console.log(e, 'load');
                        files.push(dropFile);
                        checkDropFinish();
                      }, false);
    
                      fileReader.addEventListener('error', function (e) {
                        console.log(e, 'error,不可以上传文件夹');
                        checkDropFinish();
                      }, false);
                    } catch (e) {
                      console.log(e, 'catch error,不可以上传文件夹');
                      checkDropFinish();
                    }
                  }
                }
              }
            }
          }
        },
        mounted() {
          let app = document.getElementsByClassName('app')[0];
          app.ondragstart = function (e) { // 拖拽开始
            e.preventDefault();//取消默认的链接元素和图片元素拖拽会触发拖拽上传
          };
          app.addEventListener('dragover', (e) => {  // 拖拽到另一个容器是促发
            e.preventDefault();
            this.onDragover();
          });
        }
      };
    </script>

     参照文章

    https://segmentfault.com/a/1190000013298317

  • 相关阅读:
    工具类-ApplicationContextUtil
    银行联行号-全国地区码
    银行联行号-银行编码(联行号前3位)
    前端防止 JS 调试技巧
    Vue基础框架
    关于5G手机使用4G套餐扫盲
    nginx高级-前端必会
    懒人npm运行和打包命令
    关于虚拟专用网络的一些经验
    JS加密解密
  • 原文地址:https://www.cnblogs.com/shawnyung/p/9716911.html
Copyright © 2020-2023  润新知