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)