思路:
1.文件上传之前的握手:先读取文件信息,例如文件名称、文件大小、文件MD5(用于检验上传完成后的文件完整性,以及作为当前上传的任务key)、文件分片大小、文件总片数等。
2.文件切割:按指定大小将文件切割成独立的文件片,例如2m每片。
3.文件合并:将无数个文件片合并成一个完整的文件,然后根据握手时的MD5值校验文件的完整性。
4.文件保存:将上传后的文件信息保存到数据库,然后返回文件的保存信息,比如文件路径、文件大小、文件MD5等。
5.上传成功:将上传信息返回给前端。
// 依赖spark-md5获取文件唯一的hash。npm i --save spark-md5
import SparkMD5 from 'spark-md5'
export const postConsole = (options) => {
let header = {...options.header}
return new Promise((resolve, reject) => {
uni.request({
url: options.url + '/console',
method: options.method || 'POST',
data: options.data || {},
dataType: 'json',
header,
success: (res) => {
if (res.data) {
if (res.data.code === '0') {
resolve(res.data.data)
} else {
reject(res.data.msg)
}
}
},
fail: (err) => {
reject(err)
}
})
})
}
export default class BigUpload {
constructor(Setting) {
this.Setting = Setting
}
startUpload() {
this.chunkSize = this.Setting.size
if (!this.Setting.filePath) {
return
}
this.pt_md5 = ''
this.chunks = Math.ceil(this.Setting.byteLength / this.chunkSize)
this.currentChunk = 0
this.gowith = true
this.fileSlice(0, this.Setting.byteLength, file => {
this.handshake(flag => {
if (flag) {
this.loadNext()
} else {
this.Setting.callback(false)
}
}, file)
})
}
handshake(cbk, e) {
let formData = {}
let md5 = this.getDataMd5(e)
this.pt_md5 = md5
formData.pt_md5 = this.pt_md5
formData.chunks = this.chunks
formData.size = this.Setting.byteLength
formData.type = 'handshake'
formData.md5 = md5
formData.fileName = this.Setting.fileName
formData.contentType = this.Setting.type
postConsole({
url: this.Setting.url,
data: formData
}).then(res => {
if (res === 'success') {
cbk(true)
} else if (typeof res !== 'number') {
this.Setting.callback(res)
} else {
this.currentChunk = res
if (this.currentChunk < this.chunks) {
this.loadNext()
} else {
this.currentChunk--
this.loadNext()
}
}
}).catch(err => {
console.error(err)
cbk(false)
})
}
loadNext() {
const p = this.currentChunk * 100 / this.chunks
this.drowSpeed(parseInt(p));
let start = this.currentChunk * this.chunkSize
let length = start + this.chunkSize >= this.Setting.byteLength ? this.Setting.byteLength - start : this.chunkSize
if (this.gowith) {
this.fileSlice(start, length, file => {
this.uploadFileBinary(file)
})
}
}
uploadFileBinary(data) {
const fs = uni.getFileSystemManager()
const md5 = this.getDataMd5(data)
const tempPath = `${wx.env.USER_DATA_PATH}/up_temp/${md5}.temp`
fs.access({
path: `${wx.env.USER_DATA_PATH}/up_temp`,
fail(res) {
fs.mkdirSync(`${wx.env.USER_DATA_PATH}/up_temp`, false)
}
})
fs.writeFile({
filePath: tempPath,
encoding: 'binary',
data: data,
success: res => {
let formData = {}
formData.currentChunk = this.currentChunk + 1
formData.pt_md5 = this.pt_md5
formData.type = 'file'
formData.md5 = md5
uni.uploadFile({
url: this.Setting.url,
filePath: tempPath,
name: 'file',
formData: formData,
success: res2 => {
fs.unlinkSync(tempPath)
if (res2.statusCode === 200) {
const data = JSON.parse(res2.data)
if (data.code === '0') {
this.currentChunk++
if (this.currentChunk < this.chunks) {
this.loadNext()
} else {
this.callback(data.data)
}
} else {
this.callback(false)
}
} else {
this.callback(false)
}
},
fail: err => {
console.log(err)
this.callback(false)
}
})
},
fail: err => {
console.log(err)
this.callback(false)
}
})
}
drowSpeed(p) {
if (this.Setting.drowSpeed != null && typeof (this.Setting.drowSpeed) === 'function') {
this.Setting.drowSpeed(p)
}
}
getDataMd5(data) {
if (data) {
let trunkSpark = new SparkMD5()
trunkSpark.appendBinary(data)
let md5 = trunkSpark.end()
return md5
}
}
isPlay(cbk) {
if (this.gowith) {
this.gowith = false
if (typeof (cbk) === 'function') cbk(false)
} else {
this.gowith = true
this.loadNext()
if (typeof (cbk) === 'function') cbk(true)
}
}
fileSlice(start, length, cbk) {
uni.getFileSystemManager().readFile({
filePath: this.Setting.filePath,
encoding: 'binary',
position: start,
length: length,
success: res => {
cbk(res.data)
},
fail: err => {
console.error(err)
this.callback(false)
}
})
}
callback(res) {
if (typeof (this.Setting.callback) === 'function') {
this.Setting.callback(res)
}
}
}