javascript之大文件分段上传、断点续传(一)

前言

前些时,我正在研究以post请求方式导出excel,发现了Blob这个下载文件的好帮手,于是顺手写了使用Blob对象下载文件,记录自己前进路上的点点滴滴。
服务器导出excel是将携带excel类型(MIME)的二进制字节发送到浏览器,浏览器解析该字节流以达到下载excel的目的。那么是否在上传大文件时,将大文件分解为一段一段的,然后上传至服务器,服务器将所有分解的文件组合成最初的大文件,实现大文件上传!

环境

  1. vue2.x
  2. webpack3.x
  3. axios

代码

// ArrayUtil.js
/**
 4. arraybuffer to 16进制hex字符串
 5. @param {ArrayBuffer} buf
   */
function buf2hex (buf) {
    return Array.prototype.map.call(new Uint8Array(buf), x => ('00' + x.toString(16)).slice(-2)).join('')
}
// NumberUtil.js
/**
 6. 四舍五入
 7. @param num{Number} 要处理的数字
 8. @param len{Number} 要保留的小数长度
 9. @returns {String}
   */
 function toFixed (num, len) {
    // 对整数去.0
    if (Number.isInteger(+num)) {
      num = num + ''
    }
    num = +num
    return (Number.isInteger(num) ? num : Math.round(num * Math.pow(10, len)) / Math.pow(10, len)) + ''
  }
// index.vue
import axios from 'axios'
/**
 10. 1、读取文件获取文件file和文件唯一性md5code
 */
function getFile () {
  let fileEl = this.$el.querySelector('input[type=file]')
  fileEl.addEventListener('change', e => {
    this.barVal = 0
    this.num = 1
    this.file = e.target.files[0]
    this.md5code = window.btoa(Date.now())
    this.splitFile(this.file, this.md5code)
  })
}
/**
 11. 2、文件切割
 */
function splitFile (file, md5code) {
   let self= this
   let response = null
   const {NumberUtil} = this.$util
   const reader = new FileReader()
   // 一块文件的大小
   const blockSize = 1 * 1024 * 1024
   reader.readAsArrayBuffer(file)
   reader.addEventListener('load', async e => {
     let nextSize = Math.min(this.num * blockSize, file.size)
     // 小于1M,一次上传
     if (file.size === nextSize) {
       const percent = nextSize / file.size
       const slice = e.target.result
       // 上传出错则重新上传
       try {
         response = await this.onUpload2(slice, file, md5code, percent)
       } catch (error) {
         response = await this.onUpload2(slice, file, md5code, percent)
       }
       let val = Number(response) * 100
       self.barVal = Math.min(+NumberUtil.toFixed(val, 2), 100)
       return
     }
     // 大于1M,分段上传
     while (file.size > nextSize && !this.isPause) {
       nextSize = Math.min(this.num * blockSize, file.size)
       const slice = e.target.result.slice((this.num - 1) * blockSize, nextSize)
       const percent = (blockSize * this.num) / file.size
       // 上传出错则重新上传
       try {
         response = await this.onUpload2(slice, file, md5code, percent)
       } catch (error) {
         response = await this.onUpload2(slice, file, md5code, percent)
       }
       let val = Number(response) * 100
       self.barVal = Math.min(+NumberUtil.toFixed(val, 2), 100)
       this.num++
     }
   })
 }
 // 上传
 function onUpload2 (slice, file, md5code, percent) {
      let self = this
      const {ArrayUtil} = this.$util
      let obj = {
        bufStr: ArrayUtil.buf2hex(slice),
        md5: md5code,
        filename: file.name,
        percent: percent
      }
      return new Promise((resolve, reject) => {
		axios.post({'/default/file/supUpload', obj}).then(res => {
			resolve(res.data)
		}).catch(err => {
			reject(err)
		})		
	  })
    }

效果

在这里插入图片描述

总结

  1. 使用window.btoa()方法获取此次上传文件定位唯一id
  2. 将文件转换为arraybuffer类型,再次转换为16进制字符串发送到服务器,服务器即可接受到该字符串。
  3. es7的async、await异步机制,以同步写法构建异步代码。
  4. 可上传视频、音频、压缩包等大文件。

注意

服务器在闲时合并文件用户体验更好。

参考

https://segmentfault.com/a/1190000009448892

猜你喜欢

转载自blog.csdn.net/harmsworth2016/article/details/84680383