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-07Vue全局使用less樣式,組件使用全局樣式文件中定義的變量操作
這篇文章主要介紹了Vue全局使用less樣式,組件使用全局樣式文件中定義的變量操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-10-10Vue 無限滾動(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-03vue設(shè)置路由title,但刷新頁面時(shí)title失效的解決
這篇文章主要介紹了vue設(shè)置路由title,但刷新頁面時(shí)title失效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06