您好,登錄后才能下訂單哦!
這篇文章主要介紹了web前端大文件上傳與下載問題怎么解決的相關(guān)知識,內(nèi)容詳細(xì)易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇web前端大文件上傳與下載問題怎么解決文章都會有所收獲,下面我們一起來看看吧。
日常業(yè)務(wù)中難免出現(xiàn)前端需要向后端傳輸大型文件的情況,這時單次的請求不能滿足傳輸大文件的需求,就需要用到分片上傳
業(yè)務(wù)需求為:用戶可以上傳小于20G的鏡像文件,并進(jìn)顯示當(dāng)前上傳進(jìn)度
前端:vue3.x+Element Plus組件+axios
解決思路簡單為前端選擇文件后讀取到文件的基本信息,包括:文件的大小、文件格式等信息,用于前端校驗(yàn),校驗(yàn)完成后將文件進(jìn)行切片并通過請求輪詢把切片傳遞給后端
Vue的元素代碼如下,主要借助el-upload組件:
<template> ... <!-- 文件上傳 --> <el-upload :show-file-list="false" action class="mirror-upload" :http-request="putinMirror" > <button>上傳環(huán)境鏡像</button> </el-upload> ... <!-- 進(jìn)度顯示 --> <el-progress :percentage="progress" :indeterminate="true" /> ... </template> <script setup> // 引入封裝好的接口api,根據(jù)提供的接口文檔自行封裝即可 import { // 普通get請求api checkMirrorFileApi, // 普通post請求api uploadShardFileApi, } from "@/assets/api/uploadApi.js" import { ref } from 'vue' // 文件輸進(jìn)度條 const progress = ref(0) ... </script>
配合組件選取需要上傳的文件
/* 上傳環(huán)境鏡像 分片上傳 */ const putinMirror = async (file) => { // 校驗(yàn)文件是否符合規(guī)范(注意這里的異步方法,因?yàn)檎{(diào)用了接口加上await,校驗(yàn)函數(shù)若不調(diào)用接口可以不寫await,否則返回promise對象) if (await checkMirrorFile(file)) { // 文件相關(guān)信息 let files = file.file // 從0開始的切片 let shardIndex = 0 // 調(diào)用切片方法 uploadFile(files, shardIndex) } }
這一步可以根據(jù)需求來進(jìn)行校驗(yàn),這里需要通過接口校驗(yàn)當(dāng)前服務(wù)器可用的磁盤容量來判斷是否有足夠的空間用于存放將要上傳的文件
/* 校驗(yàn)上傳鏡像文件是否符合規(guī)范 */ const checkMirrorFile = async (file) => { // 校驗(yàn)文件格式是否正確,支持.acow2/.iso/.ovf/.zip/.tar let fileType = file.file.name.split('.') if (fileType[fileType.length - 1] !== 'acow2' && fileType[fileType.length - 1] !== 'iso' && fileType[fileType.length - 1] !== 'ovf' && fileType[fileType.length - 1] !== 'zip' && fileType[fileType.length - 1] !== 'tar') { ElMessage.warning('文件格式錯誤,僅支持.acow2/.iso/.ovf/.zip/.tar') return false } // 校驗(yàn)文件大小是否滿足 let fileSize = file.file.size //文件大小是否超出20G if (fileSize > 20 * 1024 * 1024 * 1024) { ElMessage.warning('上傳文件大小不超過20G') return false } const res = await checkMirrorFileApi() if (res.code !== 200) { ElMessage.warning('暫時無法查看磁盤可用空間,請重試') return false } // 查看磁盤容量大小 if (res.data.diskDevInfos && res.data.diskDevInfos.length > 0) { let saveSize = 0 res.data.diskDevInfos.forEach(i => { // 磁盤空間賦值 if (i.devName === '/dev/mapper/centos-root') { // 返回值為GB,轉(zhuǎn)為字節(jié)B saveSize = i.free * 1024 * 1024 * 1024 } }) // 上傳的文件大小沒有超出磁盤可用空間 if (fileSize < saveSize) { return true } else { ElMessage.warning('文件大小超出磁盤可用空間容量') return false } } else { ElMessage.warning('文件大小超出磁盤可用空間容量') return false } }
校驗(yàn)完成后就可以進(jìn)行文件的切片上傳了,這里用的類似接口輪詢的方式,每次攜帶一個切片信息給后端,后端接受到切片并返回成功狀態(tài)碼后再進(jìn)行下一次切片的上傳,代碼如下:
當(dāng)然這里后端沒有過多的做切片的處理,可以通過前端使用多線程,或者不等接口響應(yīng)成功就進(jìn)行下一次傳遞切片的過程進(jìn)行上傳的提速,這里具體怎么實(shí)現(xiàn)看業(yè)務(wù)需求或者怎么配合上傳
/* 文件切片上傳 */ const uploadFile = async (file, shardIndex, createTime, savePath, relativePath, timeMillis) => { // 文件名 let name = file.name // 文件大小 let size = file.size // 分片大小 let shardSize = 1024 * 1024 * 5 // 分片總數(shù) let shardTotal = Math.ceil(size / shardSize) if (shardIndex >= shardTotal) { isAlive.value = false progress.value = 100 return } // 文件開始結(jié)束的位置 let start = shardIndex * shardSize let end = Math.min(start + shardSize, size) // 開始切割 let packet = file.slice(start, end) let formData = new FormData() formData.append("file", packet) formData.append("fileName", name) formData.append("size", size) formData.append("shardIndex", shardIndex) formData.append("shardSize", shardSize) formData.append("shardTotal", shardTotal) // 下面這些值是后端組裝切片時需要的,跟前端關(guān)系不大 if (createTime) formData.append("createTime", createTime) if (savePath) formData.append("savePath", savePath) if (shardIndex < shardTotal) { // 進(jìn)度條保留兩位小數(shù)展示 progress.value = ((shardIndex / shardTotal) * 100).toFixed(2) * 1 const res = await uploadShardFileApi(formData) if (res.code !== 200) { ElMessage.error(res.msg) progress.value = 0 return } if (res.msg == '上傳成功' && res.data.fileName && res.data.filePath) { // 這里為所有切片上傳成功后進(jìn)行的操作 ... } shardIndex++ uploadFile(file, shardIndex, res.data.createTime, res.data.savePath, res.data.relativePath, res.data.timeMillis) } }
最后將1、2、3中的代碼合起來就是完整的分片上傳了(前端帶有文件校驗(yàn))
首先就是需要配置一下Nginx,我這里的每一個切片文件的大小為5MB,但是上傳第一片的時候會報(bào)413的狀態(tài)碼,因?yàn)镹ginx默認(rèn)上傳文件的大小是1M,所以叫運(yùn)維或者后端同學(xué)改一下配置參數(shù),保證文件傳輸時不會受到服務(wù)器的限制
這里簡單說一下業(yè)務(wù)中遇到的大文件下載,上述鏡像文件上傳之后是支持用戶下載的,所以怎樣處理20G文件的下載也是需要考慮的,我與后端小伙伴嘗試過通過range推流的方式來處理大文件的下載,當(dāng)下載時除了控制臺能看到后一直在推流過來,界面上不會出現(xiàn)下載進(jìn)度的窗口,而且當(dāng)文件大小超過2G時會出現(xiàn)瀏覽器緩存不足導(dǎo)致推流的中斷,這里沒有系統(tǒng)研究具體原因
解決方法是后端直接將文件所存的地址返回給我,當(dāng)然也可以通過Nginx配置訪問到文件存儲的位置也可以,前端則通過創(chuàng)建a標(biāo)簽的方式進(jìn)行下載,這樣可以直接調(diào)用到瀏覽器自帶的下載直接顯示出來當(dāng)前文件下載的相關(guān)信息:下載進(jìn)度、傳輸?shù)乃俾屎臀募笮?,用戶體驗(yàn)更好,代碼如下:
const downloadMirror = async (item) => { let t = { id: item.id, } const res = await downloadMirrorApi(t) if (res.headers["content-disposition"]) { let temp = res.headers["content-disposition"].split(";")[1].split("filename=")[1] let fileName = decodeURIComponent(temp) // 通過創(chuàng)建a標(biāo)簽實(shí)現(xiàn)文件下載 let link = document.createElement('a') link.download = fileName link.style.display = 'none' link.href = res.data.msg document.body.appendChild(link) link.click() document.body.removeChild(link) } else { ElMessage({ message: '該文件不存在', type: 'warning', }) } }
關(guān)于“web前端大文件上傳與下載問題怎么解決”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“web前端大文件上傳與下載問題怎么解決”知識都有一定的了解,大家如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。