前言
前些时,我正在研究以post请求方式导出excel,发现了Blob这个下载文件的好帮手,于是顺手写了使用Blob对象下载文件,记录自己前进路上的点点滴滴。
服务器导出excel是将携带excel类型(MIME)的二进制字节发送到浏览器,浏览器解析该字节流以达到下载excel的目的。那么是否在上传大文件时,将大文件分解为一段一段的,然后上传至服务器,服务器将所有分解的文件组合成最初的大文件,实现大文件上传!
环境
- vue2.x
- webpack3.x
- 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)
})
})
}
效果
总结
- 使用window.btoa()方法获取此次上传文件定位唯一id
- 将文件转换为arraybuffer类型,再次转换为16进制字符串发送到服务器,服务器即可接受到该字符串。
- es7的async、await异步机制,以同步写法构建异步代码。
- 可上传视频、音频、压缩包等大文件。
注意
服务器在闲时合并文件用户体验更好。