您好,登錄后才能下訂單哦!
今天小編給大家分享一下Vue如何實現封裝一個切片上傳組件的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
單文件切片上傳
多文件切片上傳
組件使用案例
<template> <div id="app"> <div class="upload-wrap"> <UploadSlice :action="uploadInfoSlice.actionChunk" :headers="uploadInfoSlice.headers" :limit="uploadInfoSlice.limit" :accept="uploadInfoSlice.accept" :show-file-list="false" cancelable :on-success="handleSuccess" :on-remove="handleRemove" :on-cancel="handleCancel" :on-upload-pre="handleUploadPre" :on-upload-merge="handleUploadMerge" :on-form-data="genFormData" /> </div> </div> </template> <script> import UploadSlice from './components/UploadSlice.vue' import { uploadPre, uploadMerge } from '@/api/upload-slice' export default { name: 'App', components: { UploadSlice }, data() { return { // 上傳部分 uploadInfoSlice: { actionChunk: process.env.VUE_APP_BASE_API + '/storage/file/v3/chunk', // 切片請求上傳路徑 headers: { 'Authorization': 'Bearer XXX' } } } }, methods: { // 分片預請求 async handleUploadPre(file) { const form = new FormData() form.append('fileSource', 'APPLICATION') form.append('originFileName', file.name) let res = '' try { res = await uploadPre(form) } catch (error) { throw new Error(error) } }, // 構造分片參數 genFormData(chunks, uid) { const prepareId = this.getCurrentPrepareId(uid) return chunks.map(chunk => { const form = new FormData() form.append('chunk', chunk.file) form.append('uploadId', prepareId) form.append('partNumber', chunk.index) return form }) }, // 合并請求 async handleUploadMerge(file, uid) { const prepareId = this.getCurrentPrepareId(uid) const form = new FormData() form.append('fileSource', 'APPLICATION') form.append('hash', prepareId) form.append('filename', file.name) // return 建議使用, 用于handleSuccess獲取數據 try { const res = await uploadMerge(form) return res } catch (error) { return error } }, // 判斷當前處理prepareId getCurrentPrepareId(uid) { for (const item of this.progressFileList) { if (item.uid === uid) { return item.prepareId } } } } } </script>
Attribute
標紅色
部分為二次封裝處理過的功能,其他為el-upload自帶屬性
參數 | 說明 | 類型 | 可選值 | 默認值 | 備注 |
---|---|---|---|---|---|
action | 必選參數,分片上傳的地址,預請求和合并請求在組件外操作 | String | - | - | |
headers | 設置上傳的請求頭部 | String | - | - | |
multiple | 是否支持多選文件 | boolean | - | ||
accept | 可上傳文件類型,多種類型用","分隔 (格式不符合自動提示) | String | - | - | |
on-remove | 文件列表移除文件時的鉤子 | function(file, fileList) | — | — | |
on-success | 文件上傳成功時的鉤子 | function(response, file, fileList) | — | — | |
on-error | 文件上傳失敗時的鉤子 | function(err, file, fileList) | — | — | |
on-progress | 文件上傳時的鉤子 | function(event, file, fileList) | — | — | |
on-change | 文件狀態改變時的鉤子,添加文件、上傳成功和上傳失敗時都會被調用 | function(file, fileList) | — | — | |
on-exceed | 文件超出個數限制時的鉤子 | function(files, fileList) | — | — | |
list-type | 文件列表的類型 | string | text/picture/picture-card | text | |
show-file-list | 是否顯示已上傳文件列表(文件分片上傳時建議設置false,否則會有兩個進度條) | boolean | — | true | |
file-list | 上傳的文件列表, 例如: [{name: 'food.jpg', url: 'xxx.cdn.com/xxx.jpg'}] | array | — | [] | |
disabled | 是否禁用 | boolean | — | false | |
cancelable | 是否支持取消 | boolean | — | false | |
limit | 最多允許上傳個數(超出個數自動提示) | number | — | — | |
size | 限制大小 | String | — | — | |
hideBtn | 是否在上傳過程中隱藏上傳按鈕 | boolean | — | — | false |
Slot
插槽名 | 說明 |
---|---|
trigger | 觸發文件選擇框的內容 |
tip | 提示說明文字 |
more-tips | 在默認提示后補充說明 |
切片上傳組件是基于el-upload
進行的二次封裝,文章開頭組件效果演示可以看到上傳一個文件會發送三個請求:prepare,chunk, merge
,也就是整個上傳過程,主要分為三步:1.預請求 2.分片請求 3.合并請求,預請求和合并請求就是我們正常的http請求,主要處理的是分片請求,分片請求主要的步驟是:
將文件切片
構造切片請求參數
控制分片請求的并發
在el-upload
上傳后, 在on-change
屬性的回調里可以獲取文件file
,通過file.raw.slice
對文件進行切片,目前的切片規則是:1.小于10M 固定一片 2.小于50M 文件10%為一片 3.大于50M 固定5M 一片(可以根據自己的需求進行修改)
genFileChunks(file) { const chunks = [] let cur = 0 // 小于10M 固定一片 if (file.size < (10 * 1024 * 1024)) { chunks.push({ index: cur, file: file.raw.slice(cur, file.size), originFilename: file.name }) return chunks } // 小于50M 文件10%為一片 if (file.size < (50 * 1024 * 1024)) { const chunkSize = parseInt(file.size * 0.1) while (cur < file.size) { chunks.push({ index: cur, file: file.raw.slice(cur, cur + chunkSize), originFilename: file.name }) cur += chunkSize } return chunks } // 大于50M 固定5M 一片 const chunkSize = parseInt(5 * 1024 * 1024) while (cur < file.size) { chunks.push({ index: cur, file: file.raw.slice(cur, cur + chunkSize), originFilename: file.name }) cur += chunkSize } return chunks },
一個32M的文件按照10%切一片,構造好的切片數據是這樣的
切片請求不同業務的參數是變化的,所以參數部分可以拋出給父組件處理,增加組件的復用性
父組件
<template> <UploadSlice :action="uploadInfoSlice.actionChunk" :headers="uploadInfoSlice.headers" :on-form-data="genFormData" /> </template> <script> methods: { // 構造分片參數 genFormData(chunks, uid) { const prepareId = this.getCurrentPrepareId(uid) return chunks.map(chunk => { const form = new FormData() form.append('chunk', chunk.file) form.append('uploadId', prepareId) form.append('partNumber', chunk.index) return form }) }, }, </script>
子組件
<template> <el-upload action="" :accept="accept" > </template> <script> props: { onFormData: { type: Function, default: () => {} }, }, methods: { async uploadChunks(uid) { // 預請求 // --------------- // 上傳切片 const requests = this._genRequest(this._genUploadData(uid), uid) // 控制并發 await this.sendRequest(requests) // 合并請求 // --------------- }, // 構造分片參數 _genUploadData(uid) { const chunks = this.getCurrentChunks(uid) return this.onFormData(chunks, uid) }, // 生成調用請求:[Promise, Promise] _genRequest(uploadData, uid) { console.log('uploadData', uploadData) const file = this.getCurrentFile(uid) const chunks = this.getCurrentChunks(uid) return uploadData.map((form, index) => { const options = { headers: this.$attrs.headers, file: file, data: form, action: this.action, onProgress: progress => { chunks[index].progress = Number( ((progress.loaded / progress.total) * 100).toFixed(2) ) this.handleProgress(progress, file, uid) } } return options }) }, }, </script>
切片上傳如果不控制并發,在分片很多時,就會同時發送很多個http請求,導致線程阻塞,影響頁面其他請求的操作,所以控制并發是需要的。我設置的是最多允許3個并發請求。
sendRequest(requests, limit = 3) { return new Promise((resolve, reject) => { const len = requests.length let counter = 0 let isTips = false // 只提示一次失敗 let isStop = false // 如果一個片段失敗超過三次 認為當前網洛有問題 停止全部上傳 const startRequest = async() => { if (isStop) return const task = requests.shift() if (task && task.file.status !== 'cancel') { // 利用try...catch捕獲錯誤 try { // 具體的接口 抽離出去了 await ajax(task) if (counter === len - 1) { // 最后一個任務 resolve() } else { // 否則接著執行 counter++ startRequest() // 啟動下一個任務 } } catch (error) { // 網絡異常 if (error === 'NETWORK_ERROR' && !isTips) { Message.error('網絡異常,文件上傳失敗') this.upLoading = false this.preLoading = false isTips = true this.handleRemove('', []) } // 接口報錯重試,限制為3次 if (task.error < 3) { task.error++ requests.unshift(task) startRequest() } else { isStop = true reject(error) } } } } // 啟動任務 while (limit > 0) { // 模擬不同大小啟動 setTimeout(() => { startRequest() }, Math.random() * 2000) limit-- } }) } }
以上就是“Vue如何實現封裝一個切片上傳組件”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。