小程序大文件分片上传

思路:

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)
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容