Vue+ElementUI創(chuàng)建一個(gè)帶有進(jìn)度顯示的文件下載和打包組件功能
在現(xiàn)代前端開發(fā)中,用戶體驗(yàn)至關(guān)重要,尤其是在處理文件下載時(shí)。為用戶提供實(shí)時(shí)的下載進(jìn)度顯示和打包功能,不僅能提升用戶體驗(yàn),還能使應(yīng)用更具專業(yè)性。在本文中,我們將創(chuàng)建一個(gè) Vue 組件,用于顯示文件下載進(jìn)度,并將下載的文件打包成 ZIP 供用戶下載。
前言
在應(yīng)用中處理多個(gè)文件的下載時(shí),用戶希望看到清晰的進(jìn)度顯示,同時(shí)在下載多個(gè)文件時(shí),將它們打包成 ZIP 文件提供下載也是常見需求。通過使用 Vue 和一些實(shí)用的 JavaScript 庫,我們可以輕松實(shí)現(xiàn)這一功能。
1. 組件功能概述
我們要實(shí)現(xiàn)的 Vue 組件具備以下功能:
- 下載多個(gè)文件,并實(shí)時(shí)顯示每個(gè)文件的下載進(jìn)度。
- 打包下載的文件為 ZIP 并顯示打包進(jìn)度。
- 在進(jìn)度條內(nèi)同時(shí)顯示文件名稱和下載百分比。
- 下載完成后自動(dòng)關(guān)閉對(duì)話框。

2. 導(dǎo)入所需的包
在開始構(gòu)建組件之前,我們需要先導(dǎo)入一些關(guān)鍵的 JavaScript 庫。這些庫將幫助我們實(shí)現(xiàn)文件下載、ZIP 打包和文件保存功能。
npm install axios jszip file-saver --save
- axios: 用于執(zhí)行 HTTP 請(qǐng)求,并獲取文件數(shù)據(jù)。
- jszip: 用于在前端生成 ZIP 文件。
- file-saver: 用于觸發(fā)瀏覽器下載功能,保存生成的 ZIP 文件。
在 Vue 組件中,我們可以通過以下方式導(dǎo)入這些庫:
import axios from 'axios'
import JSZip from 'jszip'
import { saveAs } from 'file-saver'3. 構(gòu)建基礎(chǔ)組件
我們從構(gòu)建基礎(chǔ)的 Vue 組件結(jié)構(gòu)開始,該組件將接收文件列表和控制對(duì)話框的顯示狀態(tài)。
<template>
<div>
<el-dialog :visible.sync="internalDialogVisible" title="Download Files">
<el-progress v-for="(file, index) in downloadFiles"
style="margin-bottom: 10px;"
:key="index"
:percentage="file.progress"
:text-inside="true"
:stroke-width="26"
:format="formatProgress(file.name)">
</el-progress>
<el-progress :percentage="packProgress"
:stroke-width="26"
:text-inside="true"
status="success"
:format="formatPackingProgress">
</el-progress>
</el-dialog>
</div>
</template>4.實(shí)現(xiàn)文件下載與進(jìn)度顯示
在文件下載過程中,我們需要實(shí)時(shí)更新進(jìn)度條,并顯示文件的下載進(jìn)度。為此,axios 提供了 onDownloadProgress 鉤子,用于在下載過程中獲取進(jìn)度信息。
const response = await axios.get(file.url, {
responseType: 'blob',
onDownloadProgress: (progressEvent) => {
// 計(jì)算下載進(jìn)度
const progress = Math.round((progressEvent.loaded * 100) / progressEvent.total)
this.$set(this.downloadFiles, index, {
...file,
progress
})
}
})5. 打包文件為 ZIP
在所有文件下載完成后,我們將這些文件打包成 ZIP 格式。通過 JSZip 庫,我們可以輕松實(shí)現(xiàn)文件打包,并使用 file-saver 來觸發(fā)瀏覽器下載。
async startDownload () {
try {
const zip = new JSZip()
// 下載所有文件并添加到 zip 中
const downloadPromises = this.downloadFiles.map(async (file) => {
const response = await axios.get(file.url, {
responseType: 'blob'
})
zip.file(file.name, response.data)
})
// 等待所有文件下載完成
await Promise.all(downloadPromises)
// 生成 zip 并觸發(fā)下載
zip.generateAsync({ type: 'blob' }).then((content) => {
saveAs(content, this.zipName + '.zip')
this.$emit('update:dialogVisible', false)
})
} catch (error) {
console.error('Download failed:', error)
}
}6. 控制對(duì)話框的顯示與隱藏
為了更好地控制對(duì)話框的顯示與隱藏,我們?cè)诮M件中使用 internalDialogVisible 作為內(nèi)部控制變量,并通過 watch 監(jiān)聽 dialogVisible 的變化。當(dāng)對(duì)話框打開時(shí),我們啟動(dòng)下載流程,并在所有操作完成后關(guān)閉對(duì)話框。
watch: {
dialogVisible (newVal) {
this.internalDialogVisible = newVal
if (newVal) {
this.downloadFiles = this.files.map(file => ({ ...file, progress: 0 }))
this.packProgress = 0
this.startDownload()
}
},
internalDialogVisible (newVal) {
this.$emit('update:dialogVisible', newVal)
}
}7. 可能會(huì)遇到的問題:處理跨域請(qǐng)求 (CORS)
在實(shí)現(xiàn)文件下載功能時(shí),尤其是當(dāng)我們從不同的域名或服務(wù)器請(qǐng)求文件資源時(shí),可能會(huì)遇到跨域資源共享 (CORS) 問題。CORS 是一種瀏覽器安全機(jī)制,用于防止惡意網(wǎng)站在用戶不知情的情況下發(fā)起跨域請(qǐng)求。因此,當(dāng)我們的 Vue 應(yīng)用從與其源不同的服務(wù)器請(qǐng)求資源時(shí),瀏覽器會(huì)根據(jù) CORS 規(guī)則來決定是否允許該請(qǐng)求。
7.1 什么是 CORS?
CORS (Cross-Origin Resource Sharing) 是瀏覽器和服務(wù)器之間的一種協(xié)議,用于控制哪些資源可以通過跨域 HTTP 請(qǐng)求被訪問。它通過添加特定的 HTTP 頭來告知瀏覽器資源是否可以被訪問。
7.2 常見的 CORS 錯(cuò)誤
當(dāng)我們?cè)趯?shí)現(xiàn)文件下載時(shí),如果服務(wù)器沒有正確配置 CORS,我們可能會(huì)遇到以下錯(cuò)誤:
Access to XMLHttpRequest at 'https://example.com/file' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
這個(gè)錯(cuò)誤說明目標(biāo)服務(wù)器沒有正確配置 Access-Control-Allow-Origin 頭,導(dǎo)致瀏覽器阻止了該請(qǐng)求。
7.3 解決 CORS 問題的方法
服務(wù)器端配置:
在理想情況下,我們應(yīng)當(dāng)擁有對(duì)目標(biāo)服務(wù)器的控制權(quán),并在服務(wù)器端配置允許的跨域請(qǐng)求。具體方法是在服務(wù)器的響應(yīng)頭中添加 Access-Control-Allow-Origin,比如:
Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization
這將允許所有來源的跨域請(qǐng)求。當(dāng)然,我們也可以將 Access-Control-Allow-Origin 設(shè)置為特定的域名,以限制允許跨域訪問的來源。
使用代理服務(wù)器:
如果我們無法控制目標(biāo)服務(wù)器的配置,可以考慮在開發(fā)環(huán)境中使用代理服務(wù)器。通過 Vue CLI 我們可以輕松配置代理,將請(qǐng)求通過代理服務(wù)器發(fā)送,從而避免 CORS 問題。以下是 Vue CLI 中的 vue.config.js 配置示例:
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'https://example.com',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
}
}
}通過這種配置,所有發(fā)往 /api 的請(qǐng)求都會(huì)被代理到 https://example.com,從而避免直接跨域請(qǐng)求導(dǎo)致的 CORS 問題。
使用 JSONP:
JSONP (JSON with Padding) 是一種傳統(tǒng)的跨域解決方案,主要用于 GET 請(qǐng)求。然而,由于它僅支持 GET 請(qǐng)求且具有安全風(fēng)險(xiǎn),現(xiàn)代應(yīng)用中較少使用。
啟用 CORS 插件:
在開發(fā)環(huán)境中,為了臨時(shí)解決 CORS 問題,可以使用瀏覽器插件如 CORS Unblock。不過,這只適用于開發(fā)和調(diào)試階段,不推薦在生產(chǎn)環(huán)境中使用。
7.4 說明
CORS 問題是開發(fā)跨域資源請(qǐng)求時(shí)不可避免的挑戰(zhàn)之一。在實(shí)現(xiàn)文件下載功能時(shí),務(wù)必確保服務(wù)器配置正確的 CORS 頭,以允許來自我們應(yīng)用的請(qǐng)求。如果無法控制服務(wù)器配置,可以考慮使用代理服務(wù)器或其他臨時(shí)解決方案。在生產(chǎn)環(huán)境中,最好的做法是從服務(wù)器端正確處理 CORS,以確保安全性和可靠性。
8. 完整源代碼
<template>
<div>
<el-dialog :visible.sync="internalDialogVisible"
title="Download Files">
<el-progress v-for="(file, index) in downloadFiles"
style="margin-bottom: 10px;"
:key="index"
:percentage="file.progress"
:text-inside="true"
:stroke-width="26"
:format="formatProgress(file.name)">
</el-progress>
<el-progress :percentage="packProgress"
:stroke-width="26"
:text-inside="true"
status="success"
:format="formatPackingProgress">
</el-progress>
</el-dialog>
</div>
</template>
<script>
import axios from 'axios'
import JSZip from 'jszip'
import { saveAs } from 'file-saver'
export default {
name: 'DownloadManager',
props: {
files: {
type: Array,
required: true
},
dialogVisible: {
type: Boolean,
required: true
},
zipName: {
type: String,
required: true
}
},
data () {
return {
internalDialogVisible: this.dialogVisible,
downloadFiles: this.files.map(file => ({ ...file, progress: 0 })),
packProgress: 0
}
},
methods: {
formatProgress (fileName) {
return percentage => `${fileName} - ${percentage}%`
},
formatPackingProgress (percentage) {
return `Packing Files - ${percentage}%`
},
async startDownload () {
try {
const zip = new JSZip()
// 下載所有文件并添加到 zip 中
const downloadPromises = this.downloadFiles.map(async (file, index) => {
const response = await axios.get(file.url, {
responseType: 'blob',
onDownloadProgress: (progressEvent) => {
// 計(jì)算下載進(jìn)度
const progress = Math.round((progressEvent.loaded * 100) / progressEvent.total)
this.$set(this.downloadFiles, index, {
...file,
progress
})
}
})
zip.file(file.name, response.data)
})
// 等待所有文件下載完成
await Promise.all(downloadPromises)
// 生成 zip 并觸發(fā)下載,同時(shí)更新打包進(jìn)度
zip.generateAsync({ type: 'blob' }, (metadata) => {
this.packProgress = Math.round((metadata.percent))
}).then((content) => {
saveAs(content, this.zipName + '.zip')
this.$emit('update:dialogVisible', false)
})
} catch (error) {
console.error('Download failed:', error)
}
}
},
watch: {
dialogVisible (newVal) {
this.internalDialogVisible = newVal
if (newVal) {
this.downloadFiles = this.files.map(file => ({ ...file, progress: 0 }))
this.packProgress = 0
this.startDownload()
}
},
internalDialogVisible (newVal) {
this.$emit('update:dialogVisible', newVal)
}
}
}
</script>9. 組件調(diào)用
要在外部調(diào)用這個(gè)組件,可以按照以下步驟操作:
9.1 引入并注冊(cè)組件
在希望使用這個(gè) DownloadManager 組件的地方引入它,并在父組件中注冊(cè)。
假設(shè)在一個(gè)名為 App.vue 的組件中調(diào)用 DownloadManager:
<template>
<div>
<!-- 其他內(nèi)容 -->
<!-- 使用 DownloadManager 組件 -->
<DownloadManager
:files="filesToDownload"
:dialogVisible.sync="isDialogVisible"
:zipName="zipFileName"
/>
<!-- 觸發(fā)下載對(duì)話框顯示的按鈕 -->
<el-button @click="openDownloadDialog">Download Files</el-button>
</div>
</template>
<script>
import DownloadManager from './components/DownloadManager.vue' // 根據(jù)實(shí)際路徑調(diào)整
export default {
components: {
DownloadManager
},
data () {
return {
filesToDownload: [
{ name: 'file1.txt', url: 'https://example.com/file1.txt' },
{ name: 'file2.txt', url: 'https://example.com/file2.txt' }
],
isDialogVisible: false,
zipFileName: 'downloaded_files'
}
},
methods: {
openDownloadDialog() {
this.isDialogVisible = true
}
}
}
</script>9.2 組件參數(shù)以及解釋
files: 傳遞一個(gè)文件數(shù)組,每個(gè)文件對(duì)象應(yīng)包含name和url屬性,用于下載文件。dialogVisible: 控制el-dialog的可見性,使用.sync修飾符讓父組件與子組件之間雙向綁定。zipName: 傳遞生成的 ZIP 文件名。
使用 DownloadManager 組件時(shí),通過綁定屬性 (:files, :dialogVisible.sync, :zipName) 來控制組件行為。
調(diào)用 openDownloadDialog 方法顯示下載對(duì)話框,并開始下載文件。
總結(jié)
通過本文,我們學(xué)習(xí)了如何使用 Vue 創(chuàng)建一個(gè)帶有進(jìn)度顯示和打包功能的文件下載組件。我們探討了如何導(dǎo)入必要的包,構(gòu)建組件的基礎(chǔ)結(jié)構(gòu),實(shí)現(xiàn)文件下載與進(jìn)度顯示,以及如何將文件打包為 ZIP 格式供用戶下載。這種組件不僅能提升用戶體驗(yàn),還能被復(fù)用于各種場(chǎng)景,為我們的項(xiàng)目增色不少。
我們可以根據(jù)具體需求進(jìn)一步擴(kuò)展這個(gè)組件,比如添加錯(cuò)誤處理、取消下載等功能。
到此這篇關(guān)于Vue+ElementUI創(chuàng)建一個(gè)帶有進(jìn)度顯示的文件下載和打包組件功能的文章就介紹到這了,更多相關(guān)Vue ElementUI文件下載進(jìn)度內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue3+vite加載本地js/json文件不能使用require淺析
這篇文章主要給大家介紹了關(guān)于vue3+vite加載本地js/json文件不能使用require的相關(guān)資料,require 是屬于 Webpack 的方法,在v3+vite的項(xiàng)目里面并不適用,需要的朋友可以參考下2023-07-07
Vue全局使用less樣式,組件使用全局樣式文件中定義的變量操作
這篇文章主要介紹了Vue全局使用less樣式,組件使用全局樣式文件中定義的變量操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-10-10
Vue 無限滾動(dòng)加載指令實(shí)現(xiàn)方法
這篇文章主要介紹了Vue 無限滾動(dòng)加載指令的實(shí)現(xiàn)代碼,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2019-05-05
在vue使用echarts報(bào)錯(cuò):invalid dom問題
這篇文章主要介紹了在vue使用echarts報(bào)錯(cuò):invalid dom問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
vue設(shè)置路由title,但刷新頁面時(shí)title失效的解決
這篇文章主要介紹了vue設(shè)置路由title,但刷新頁面時(shí)title失效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06

