基於vue的h5文件切片上傳(獲取文件md5,實現秒傳、進度條實現)

templatejavascript

<button @click="file"></button>
<label ref="upload"
       style="position: relative;">
  <input type="file"
         @change="selectFile"
         style="position: absolute; width: 1px; height: 1px; opacity: 0; z-index: -1;">
</label>

scripthtml

methods: {
    file() {
        // 模擬點擊file input觸發選擇文件,注意:不能在任何方式的回調裏面執行此語句
        this.$refs.upload.click()
    }
    selectFile(event) {
        // 調用上傳方法,傳入選擇的文件對象
        this.uploadFile(event.target.files[0], () => {
            // upload-success
        })
        // 重置file input控件的值
        event.target.value = ''
    }
}

核心js文件vue

// uploadFile.js
import Vue from 'vue'
import SparkMD5 from 'spark-md5'

export default function() {

    // 將上傳文件的方法掛載到vue的原型鏈上面
    Vue.prototype.uploadFile = uploadFile

    function uploadFile(file, backtopage) {

        // 獲得md5碼
        getFileMD5(file, md5 => {
            // 存儲文件的md5碼
            file.md5 = md5
            // 拿md5碼查詢後臺數據庫是否存在此md5碼,若是存在則無需上傳
            // handleAjax爲封裝好的ajax方法
            this.handleAjax('api/doc/file/getFileByMd5', {
                md5,
                name: file.name
            }, res => {
                if (!res.data) { // 不存在
                    // 開始上傳文件
                    uploadChunk(this, file, 0, backtopage)
                } else { // 秒傳
                    // 文件上傳成功
                    backtopage && backtopage()
                }
            })
        })
    }

    // currentChunk爲上傳文件塊的索引
    function uploadChunk(that, file, currentChunk, backtopage) {
        var fileReader = new FileReader(),
            // 上傳文件塊的大小,可自定義
            chunkSize = 2097152,
            // 計算改文件的可分爲多少塊
            chunks = Math.ceil(file.size / chunkSize)

        // 文件切割後的回調,this.result爲切割的文件塊
        fileReader.onload = function(e) {
            // 用FormData傳輸文件對象
            let fd = new FormData()
            // 設置文件上傳接口的須要的參數
            fd.append('md5', file.md5)
            fd.append('chunks', chunks)
            fd.append('chunksize', chunksize)
            fd.append('currentChunk', currentChunk)
            // 設置上傳的當前的文件塊
            fd.append('fileObj', new Blob([this.result]))

            let xhr = new XMLHttpRequest()
            xhr.open('post', 'api/common/file/upload', true)
            xhr.onreadystatechange = function() {
                // 上傳成功
                if (xhr.readyState == 4 && xhr.status == 200) {
                    currentChunk++
                    if (currentChunk < chunks) {
                        loadNext() // 繼續切割下一塊文件
                    } else {
                        // 文件上傳成功
                        backtopage && backtopage()
                    }
                    xhr = null
                    return
                }
                if (xhr.readyState == 4 && xhr.status == 500) {
                    // 文件上傳出錯
                }
            }
            // 文件上傳進度條
            xhr.upload.onprogress = function(e) {
                // 計算上傳的進度
                const progress = parseInt((e.loaded + currentChunk * chunkSize) / file.size * 100)
                // 更新ui
            }
            xhr.onerror = xhr.upload.onerror = function() {
                // 文件上傳出錯
            }
            //開始發送
            xhr.send(fd)
            fd = null
        }

        //處理單片文件的上傳
        function loadNext() {
            // 查詢後臺判斷當前塊文件有沒有上傳,若是已經上傳則不須要上傳,繼續讀取下一塊
            that.handleAjax('api/common/file/checkChunk', { md5: upload.md5, chunk: currentChunk }, res => {
                // 後臺返回沒有上傳過
                if (res.data === false) {
                    // 計算切割文件的開始索引和結束索引
                    var start = currentChunk * chunkSize,
                        end = Math.min(start + chunkSize, file.size)
                    // 切割文件並觸發fileReader.onload
                    fileReader.readAsArrayBuffer(file.slice(start, end))
                    // 後臺返回此塊已經上傳過,遞歸調用loadNext,繼續判斷下一塊文件塊
                } else {
                    currentChunk++
                    if (currentChunk < chunks) {
                        loadNext()
                    }
                }
            }, err => {
                // 文件上傳出錯
            })
        }
        // 觸發文件第一塊上傳
        loadNext()
    }

    // 得到文件md5
    function getFileMD5(file, callback) {
        //聲明必要的變量
        var fileReader = new FileReader(),

            //文件每塊分割2M,計算分割詳情
            chunkSize = 2097152,
            chunks = Math.ceil(file.size / chunkSize),
            currentChunk = 0,

            //建立md5對象(基於SparkMD5)
            spark = new SparkMD5()

        //每塊文件讀取完畢以後的處理
        fileReader.onload = function(e) {
            //每塊交由sparkMD5進行計算
            spark.appendBinary(e.target.result)
            currentChunk++

            //若是文件處理完成計算MD5,若是還有分片繼續處理
            if (currentChunk < chunks) {
                loadNext()
            } else {
                callback(spark.end())
            }
        }

        //處理單片文件的上傳
        function loadNext() {
            var start = currentChunk * chunkSize,
                end = start + chunkSize >= file.size ? file.size : start + chunkSize

            fileReader.readAsBinaryString(file.slice(start, end))
        }

        loadNext()

    }

}

將uploadFile.js掛載帶vue上面java

// main.js

// Import Vue
import Vue from 'vue'

// Import upload
import uploadFile from './assets/js/uploadFile'

// 把封裝好的文件下載掛載到vue上
Vue.use(downloadFile)
相關文章
相關標籤/搜索