欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

企业网盘之分片上传组件

程序员文章站 2022-07-02 17:00:12
效果展示 包含技术点 1、分片上传。 2、文件秒传。 3、文件夹上传 4、文件续传。 5、文件拖拽上传。 组件目录 实现分析 分片上传 通过H5 FileUpload对象可以实现文件上传, mutiple属性可以支持文件多选。拿到文件对象后,调用完整的分片上传流程:计算MD5-添加文件-获取鉴权信息 ......

效果展示

企业网盘之分片上传组件

 包含技术点

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>

 参照文章