前端上传,进度,预计上传完成时间

4,186 阅读3分钟

一,痛点

  • 上传进度条进度控制
  • 当前上传带宽检测,预计上传完成时间
  • 计算机存储单位之间换算和关系(Bit-比特;B-字节 ;KB-千字节 ;MB-兆字节 ;GB-吉字节 ;TB-太字节)

二,计算机存储单位之间换算和关系

上传文件少不了对计算机存储单位之间有一定了解,我们需要掌握一定的储存单位知识

位 (bit) 是计算机数据存储的最小单位。每个二进制数字0或者1就是1个位;

字节 (byte) 8个位构成一个字节;即:1 byte (字节)= 8 bit(位);

千字节 (Kb) 通常我们常见的最小单位kb就是千字节 1kb = 1024byte 即,一千字节 = 1024 字节,一千字节 = 1024*8 位(bit)

兆字节 (Mb) 兆字节是现在最普遍看到的单位,现在的通信网络带宽都是以兆字节为基础 ****

1 MB = 1024 KB ; 1 GB = 1024 MB; (2^20 B) ; 1 TB = 1024 GB; (2^30 B)

三,上传进度条控制

  1. 使用jquery.ajax 处理文件上传进度

    XMLHttpRequest.upload 向后台上传文件时监听进度,主要使用的是XMLHttpRequest提供的upload方法,此方法会返回一个XMLHttpRequestUpload对象,用来表示上传进度 。

    FormData 是XMLHttpRequest提供的一个新的接口,主要优点是可以异步上传二进制文件。

    function uploadFile() {
        //获取上传的文件
        var uploadFile = $('#upload-file').get(0).file[0];
        var formdata = new FormData(); //创建formdata对象
        formdata.append('fileInfo', uploadFile);
        $.ajax({
            url: '/上传接口',
            type: 'post',
            dataType: 'json',
            processData: false,
            contentType: false,
            xhr: function() {
                var xhr = new XMLHttpRequest();
                //使用XMLHttpRequest.upload监听上传过程,注册progress事件,打印回调函数中的event事件
                xhr.upload.addEventListener('progress', function (e) {
                    console.log(e);
                    //loaded代表上传了多少
                    //total代表总数为多少
                    var progressRate = (e.loaded / e.total) * 100 + '%';
                    //通过设置进度条的宽度达到效果
                    $('.progress > div').css('width', progressRate);
                })
    
                return xhr;
            }
        })
    
  2. 使用axios的onUploadProgress处理上传进度条

    axios的请求配置中可以设定onUploadProgress 来监听上传的进度,配置onDownloadProgress 来监听下载进度

    	// 进度条
          const uploadProgressEvent = (progressEvent) => {
            this.progressPercent = Math.floor(
              (progressEvent.loaded * 100) / progressEvent.total
            );
          };
          this.onConnectionChange();
          // 发起请求
          http({
            url: "/接口",
            method: "post",
            data: formData,
            onUploadProgress: uploadProgressEvent,
          }).then((res) => {
            if (res.code == 1) {
              ...成功处理code...
            } else {
    	        ...失败处理code...
            }
          });
    

    progressEvent.loaded表示当前上传的数据大小,progressEvent.total表示整个要上传的数据大小。拿到当前上传数据的百分比后传**progressPercent(在data中自定义)**给Element的进度条组件就可以实现啦。

    [ProgressEvent.lengthComputable](<https://developer.mozilla.org/zh-CN/docs/Web/API/ProgressEvent/lengthComputable>) 只读

    是一个 [Boolean (en-US)](developer.mozilla.org/en-US/docs/…) 标志,表示底层流程将需要完成的总工作量和已经完成的工作量是否可以计算。换句话说,它告诉我们进度是否可以被测量。

    [ProgressEvent.loaded (en-US)](developer.mozilla.org/en-US/docs/…) 只读

    是一个 unsigned long long 类型数据,表示底层流程已经执行的工作总量。可以用这个属性和 ProgressEvent.total 计算工作完成比例。当使用 HTTP 下载资源,它只表示内容本身的部分,不包括首部和其它开销。

    [ProgressEvent.total (en-US)](developer.mozilla.org/en-US/docs/…) 只读

    是一个 unsigned long long 类型数据,表示正在执行的底层流程的工作总量。当使用 HTTP 下载资源,它只表示内容本身的部分,不包括首部和其它开销。

    axios的onUploadProgress 可以理解为插件为你封装好XMLHttpRequest.upload 你只需要执行调用监听其返回值就可以

四,上传带宽检测,预计上传完成时间

我们首先要知道,计算文件预计上传完成时间,需要知道以下条件:

  • 文件大小
  • 当前上传的网络速度
  • 剩余未传文件大小
  • 当前上传进度(计算剩余未传文件需要用到)

计算文件预计上传完成时间公式:

剩余未传文件大小= 文件大小(Mb) - (当前上传进度 / 100) * 文件大小(Mb)) * 1024; (转换kb)

预计上传完成时间 = 剩余未传文件大小 / 当前上传的网络速度(kb)

最后我们得到的时间就是秒 s

文件大小 我们可以通过文件上传的回调中拿到,比如:

<el-upload
   class="upload-apk"
   :class="[progressLoad ? 'no-border' : '']"
    drag
   action=""
   :before-upload="beforePackUpload"
   :http-request="httpUploadPack"
   :multiple="false"
   :show-file-list="false"
 />

async httpUploadPack(param) {
      const { file } = param;
      this.$set(this.form, "size", file.size);
			//上传文件回调返回大小默认是kb 我们需要转成mb
      this.$set(this, "fileSizeMb", parseInt(file.size / 1024 / 1024));
}

当前网络速度,我们可以通过调用navigator.connection.downlink获取,因为该api并不是实时返回当前带宽给我们,所以我们需要设置一个定时器,定时去获取当前网络速度,去实时更新(别忘了设定关闭该定时器的条件,这里设定的是上传完成)。

//监听网络状态
    onConnectionChange() {
      this.linkSpeedTimer = setInterval(() => {
        this.uploadSpeedKb = (navigator.connection.downlink * 1024) / 8;
        this.uploadSpeed =
          (navigator.connection.downlink * 1024) / 8 > 1024
            ? (navigator.connection.downlink * 1024) / 8 / 1024 + "m/s"
            : (navigator.connection.downlink * 1024) / 8 + "kb/s";
        if (!this.progressLoad) {
          clearInterval(this.linkSpeedTimer);
        }
      }, 1000);
    },

navigator.connection.downlink 会返回以(兆比特/秒,mb/s)为单位的有效带宽估计值(参考MDN),这和我们常用的(KB/s)有所差别,所以我们需要再做一下单位换算

到这里,我们可以通过计算属性,去获取预计上传完成时间

computed: {
    planTimes() {
      let uploadSpeedKb = this.uploadSpeedKb;
      let fileSizeMb = this.fileSizeMb;
      let remainPackSize =
        (fileSizeMb - parseInt((this.progressPercent / 100) * fileSizeMb)) *
        1024;
      let remainTime = parseInt(remainPackSize / uploadSpeedKb); //秒
      if (remainTime < 60) {
        return remainTime + "秒";
      } else {
        var min_total = Math.floor(remainTime / 60) || 0; // 分钟
        var sec = Math.floor(remainTime % 60) || 0; // 余秒
        if (min_total < 60) {
          return min_total + "分钟" + sec + "秒";
        } else {
          var hour_total = Math.floor(min_total / 60) || 0; // 小时数
          var min = Math.floor(min_total % 60) || 0; // 余分钟
          return hour_total + "小时" + min + "分钟" + sec + "秒";
        }
      }
    },
  },

注意的是,navigator.connection 不支持火狐 open ie 等浏览器,详情查阅MDN

效果图: image.png