Vue+ElementUI技巧分享:创建一个带有进度显示的文件下载和打包组件

news2024/9/20 9:20:16

在现代前端开发中,用户体验至关重要,尤其是在处理文件下载时。为用户提供实时的下载进度显示和打包功能,不仅能提升用户体验,还能使应用更具专业性。在本文中,我们将创建一个 Vue 组件,用于显示文件下载进度,并将下载的文件打包成 ZIP 供用户下载。

文章目录

  • 前言
  • 1. 组件功能概述
  • 2. 导入所需的包
  • 3. 构建基础组件
  • 4.实现文件下载与进度显示
  • 5. 打包文件为 ZIP
  • 6. 控制对话框的显示与隐藏
  • 7. 可能会遇到的问题:处理跨域请求 (CORS)
    • 7.1 什么是 CORS?
    • 7.2 常见的 CORS 错误
    • 7.3 解决 CORS 问题的方法
    • 7.4 说明
  • 8. 完整源代码
  • 9. 组件调用
    • 9.1 引入并注册组件
    • 9.2 组件参数以及解释
  • 总结


前言

在应用中处理多个文件的下载时,用户希望看到清晰的进度显示,同时在下载多个文件时,将它们打包成 ZIP 文件提供下载也是常见需求。通过使用 Vue 和一些实用的 JavaScript 库,我们可以轻松实现这一功能。


1. 组件功能概述

我们要实现的 Vue 组件具备以下功能:

  • 下载多个文件,并实时显示每个文件的下载进度。
  • 打包下载的文件为 ZIP 并显示打包进度。
  • 在进度条内同时显示文件名称和下载百分比。
  • 下载完成后自动关闭对话框。
    导入进度展示

2. 导入所需的包

在开始构建组件之前,我们需要先导入一些关键的 JavaScript 库。这些库将帮助我们实现文件下载、ZIP 打包和文件保存功能。

npm install axios jszip file-saver --save
  • axios: 用于执行 HTTP 请求,并获取文件数据。
  • jszip: 用于在前端生成 ZIP 文件。
  • file-saver: 用于触发浏览器下载功能,保存生成的 ZIP 文件。

在 Vue 组件中,我们可以通过以下方式导入这些库:

import axios from 'axios'
import JSZip from 'jszip'
import { saveAs } from 'file-saver'

3. 构建基础组件

我们从构建基础的 Vue 组件结构开始,该组件将接收文件列表和控制对话框的显示状态。

<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.实现文件下载与进度显示

在文件下载过程中,我们需要实时更新进度条,并显示文件的下载进度。为此,axios 提供了 onDownloadProgress 钩子,用于在下载过程中获取进度信息。

const response = await axios.get(file.url, {
  responseType: 'blob',
  onDownloadProgress: (progressEvent) => {
    // 计算下载进度
    const progress = Math.round((progressEvent.loaded * 100) / progressEvent.total)
    this.$set(this.downloadFiles, index, {
      ...file,
      progress
    })
  }
})

5. 打包文件为 ZIP

在所有文件下载完成后,我们将这些文件打包成 ZIP 格式。通过 JSZip 库,我们可以轻松实现文件打包,并使用 file-saver 来触发浏览器下载。

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 并触发下载
    zip.generateAsync({ type: 'blob' }).then((content) => {
      saveAs(content, this.zipName + '.zip')
      this.$emit('update:dialogVisible', false)
    })
  } catch (error) {
    console.error('Download failed:', error)
  }
}

6. 控制对话框的显示与隐藏

为了更好地控制对话框的显示与隐藏,我们在组件中使用 internalDialogVisible 作为内部控制变量,并通过 watch 监听 dialogVisible 的变化。当对话框打开时,我们启动下载流程,并在所有操作完成后关闭对话框。

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. 可能会遇到的问题:处理跨域请求 (CORS)

在实现文件下载功能时,尤其是当我们从不同的域名或服务器请求文件资源时,可能会遇到跨域资源共享 (CORS) 问题。CORS 是一种浏览器安全机制,用于防止恶意网站在用户不知情的情况下发起跨域请求。因此,当我们的 Vue 应用从与其源不同的服务器请求资源时,浏览器会根据 CORS 规则来决定是否允许该请求。

7.1 什么是 CORS?

CORS (Cross-Origin Resource Sharing) 是浏览器和服务器之间的一种协议,用于控制哪些资源可以通过跨域 HTTP 请求被访问。它通过添加特定的 HTTP 头来告知浏览器资源是否可以被访问。

7.2 常见的 CORS 错误

当我们在实现文件下载时,如果服务器没有正确配置 CORS,我们可能会遇到以下错误:

  • 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.

这个错误说明目标服务器没有正确配置 Access-Control-Allow-Origin 头,导致浏览器阻止了该请求。

7.3 解决 CORS 问题的方法

  1. 服务器端配置

    • 在理想情况下,我们应当拥有对目标服务器的控制权,并在服务器端配置允许的跨域请求。具体方法是在服务器的响应头中添加 Access-Control-Allow-Origin,比如:
      Access-Control-Allow-Origin: *
      Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
      Access-Control-Allow-Headers: Content-Type, Authorization
      
      这将允许所有来源的跨域请求。当然,我们也可以将 Access-Control-Allow-Origin 设置为特定的域名,以限制允许跨域访问的来源。
  2. 使用代理服务器

    • 如果我们无法控制目标服务器的配置,可以考虑在开发环境中使用代理服务器。通过 Vue CLI 我们可以轻松配置代理,将请求通过代理服务器发送,从而避免 CORS 问题。以下是 Vue CLI 中的 vue.config.js 配置示例:
      module.exports = {
        devServer: {
          proxy: {
            '/api': {
              target: 'https://example.com',
              changeOrigin: true,
              pathRewrite: { '^/api': '' }
            }
          }
        }
      }
      
      通过这种配置,所有发往 /api 的请求都会被代理到 https://example.com,从而避免直接跨域请求导致的 CORS 问题。
  3. 使用 JSONP

    • JSONP (JSON with Padding) 是一种传统的跨域解决方案,主要用于 GET 请求。然而,由于它仅支持 GET 请求且具有安全风险,现代应用中较少使用。
  4. 启用 CORS 插件

    • 在开发环境中,为了临时解决 CORS 问题,可以使用浏览器插件如 CORS Unblock。不过,这只适用于开发和调试阶段,不推荐在生产环境中使用。

7.4 说明

CORS 问题是开发跨域资源请求时不可避免的挑战之一。在实现文件下载功能时,务必确保服务器配置正确的 CORS 头,以允许来自我们应用的请求。如果无法控制服务器配置,可以考虑使用代理服务器或其他临时解决方案。在生产环境中,最好的做法是从服务器端正确处理 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) => {
              // 计算下载进度
              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 并触发下载,同时更新打包进度
        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. 组件调用

要在外部调用这个组件,可以按照以下步骤操作:

9.1 引入并注册组件

在希望使用这个 DownloadManager 组件的地方引入它,并在父组件中注册。

假设在一个名为 App.vue 的组件中调用 DownloadManager

<template>
  <div>
    <!-- 其他内容 -->

    <!-- 使用 DownloadManager 组件 -->
    <DownloadManager
      :files="filesToDownload"
      :dialogVisible.sync="isDialogVisible"
      :zipName="zipFileName"
    />
    
    <!-- 触发下载对话框显示的按钮 -->
    <el-button @click="openDownloadDialog">Download Files</el-button>
  </div>
</template>

<script>
import DownloadManager from './components/DownloadManager.vue' // 根据实际路径调整

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 组件参数以及解释

  • files: 传递一个文件数组,每个文件对象应包含 nameurl 属性,用于下载文件。
  • dialogVisible: 控制 el-dialog 的可见性,使用 .sync 修饰符让父组件与子组件之间双向绑定。
  • zipName: 传递生成的 ZIP 文件名。

使用 DownloadManager 组件时,通过绑定属性 (:files, :dialogVisible.sync, :zipName) 来控制组件行为。
调用 openDownloadDialog 方法显示下载对话框,并开始下载文件。


总结

通过本文,我们学习了如何使用 Vue 创建一个带有进度显示和打包功能的文件下载组件。我们探讨了如何导入必要的包,构建组件的基础结构,实现文件下载与进度显示,以及如何将文件打包为 ZIP 格式供用户下载。这种组件不仅能提升用户体验,还能被复用于各种场景,为我们的项目增色不少。

我们可以根据具体需求进一步扩展这个组件,比如添加错误处理、取消下载等功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2050401.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

视图变化 - 等比例变换防止视图拉伸

文章目录 使用场景等比变换等高填充等宽填充代码进行比目标宽高计算超出部分处理设置负的 marginclip 裁剪 End参考&#xff1a; 使用场景 在日常开发中&#xff0c;经常会遇到的一个需求是将图片/视频从界面的一个位置&#xff0c;变换到另一个位置。在处理这类问题的时候经常…

基于微信小程序的书籍销售预测系统的设计与实现(论文+源码)_kaic

摘 要 随着信息化社会的进步&#xff0c;我们的生活越来越便利。在网上&#xff0c;我们可以轻松地进行各种交易&#xff0c;其中包括图书交易。可以说&#xff0c;图书交易是网络交易的一个重要方面。本系统以面向对象的方式进行开发&#xff0c;使用MySQL作为主要数据存储…

linux centos stream9图形化操作

初学者对图形化操作比较熟悉,对字符界面、命令行比较陌生。本文讨论一下图形化操作的基本技能。希望初学者掌握后尽快使用字符界面,会执行命令,更会编程。 本案例是基于stream9版本,如版本不同,会有差别,注意操作使用。 一、安装图形化界面 Linux操作系统常用的图形用…

Unity URP无光照下Shadow 制作 <二> 合批处理

闲谈 相信大家在日常工作中发现了一个问题 &#xff0c; urp下虽然可以做到3个Pass 去写我们想要的效果&#xff0c;但是&#xff0c;不能合批&#xff08;不能合批&#xff0c;那不是我们CPU要干冒烟~&#xff01;&#xff09; 好家伙&#xff0c;熊猫老师的偏方来了 &#x…

Leetcode JAVA刷刷站(38)外观数列

一、题目概述 二、思路方向 为了解决这个问题&#xff0c;我们可以编写一个Java函数countAndSay&#xff0c;该函数接受一个整数n作为输入&#xff0c;并返回外观数列的第n个元素。这个函数将基于递归公式来构建数列&#xff0c;其中countAndSay(1) "1"&#xff0c;…

vue设置环境变量

1、在根目录地下建立两个文件&#xff0c;.env.development 和 .env.production VUE_APP_BASE_API"" .env.development这个就是开发环境&#xff0c;.env.production这个就是生产环境&#xff0c;也就是这个可以写本地的ip端口 .env.production 这个就是生产环境&…

Verilog刷题笔记51

题目&#xff1a; Now that you have a state machine that will identify three-byte messages in a PS/2 byte stream, add a datapath that will also output the 24-bit (3 byte) message whenever a packet is received (out_bytes[23:16] is the first byte, out_bytes[1…

14、springboot3 vue3开发平台-前端-自定义菜单组件,根据路由动态渲染

文章目录 1. 组件2 . 使用示例 1. 组件 src\components\menuTree\index.vue <template><template v-for"item in menuList"><!-- 分为两种方式渲染&#xff1a;有子菜单和没有子菜单--><!-- 没有子菜单--><el-menu-item :index&qu…

linux cpu问题排查及性能优化

cpu性能 一、cpu啥时候才叫有瓶颈 cpu运行的快还是慢、cpu有没有问题&#xff0c;cpu是不是还需要优化。这些是平常开发和运维中经常遇到的问题。那么我门到底如何去判断机器cpu运行的到底有没有异常呢。 从我排查问题来说&#xff0c;单看系统指标不能完全反应应用运行的状态…

移动魔百盒刷机教程

准备工作 确认型号&#xff1a;首先确认您的魔百盒的具体型号&#xff0c;不同的型号可能需要不同的刷机包。刷机包&#xff1a;下载适合您型号的刷机包。U盘&#xff1a;准备一个容量不超过8GB的U盘&#xff0c;并将其格式化为FAT32格式。刷机工具&#xff1a;根据型号可能需…

深度学习实战:手把手教你构建多任务、多标签模型

多任务多标签模型是现代机器学习中的基础架构&#xff0c;这个任务在概念上很简单 -训练一个模型同时预测多个任务的多个输出。 在本文中,我们将基于流行的 MovieLens 数据集,使用稀疏特征来创建一个多任务多标签模型,并逐步介绍整个过程。所以本文将涵盖数据准备、模型构建、训…

keepalived讲解及练习

目录 1、keepalived介绍 1.1 keepalived简介 2、高可用集群 2.1 集群类型 2.2 系统可用性 2.3 系统故障 2.4 实现高可用 3、VRRP 3.1 VRRP&#xff1a;Virtual Router Redundancy Protocol 3.2 VRRP 相关术语 3.3 VRRP相关技术 4、 keepalived实验 4.1 全局配置 4…

Vue封装axios请求(超详细)

一、简介 Vue封装axios请求是指将axios库集成到Vue项目中&#xff0c;以便更方便地发送HTTP请求。首先&#xff0c;需要安装axios库&#xff0c;然后在Vue项目中创建一个名为request.js的文件&#xff0c;用于封装axios实例。在这个文件中&#xff0c;可以设置默认的配置&#…

fastzdp_sqlmodel框架是如何实现更新和删除相关的功能封装的,20240817,Python的国产新ORM框架

根据模型对象更新 初步封装的方法 def update(engine, model_obj, update_dict):"""修改数据:param engine: 连接数据库的引擎对象:param model_obj: 模型对象:param update_dict: 更新字典:return:"""with Session(engine) as session:if not…

Git工具详细使用教程

Git工具详细使用教程 Git是一个分布式版本控制系统&#xff0c;它可以帮助你管理代码的历史记录。本教程将介绍如何使用Git工具进行基本的版本控制操作。 1. 安装Git 首先&#xff0c;你需要在你的计算机上安装Git。你可以从Git官方网站&#xff08;https://git-scm.com/&am…

MySQL(三)——DCL

文章目录 DCL用户管理查询用户创建用户修改用户密码删除用户 权限控制查询权限授予权限撤销权限 DCL DCL&#xff08;Data Control Language&#xff0c;数据控制语言&#xff09;是SQL的一个子集&#xff0c;专门用于定义数据库、表、视图等的访问权限和安全级别。 它允许数据…

Openstack二层网络的构建和使用

Openstack二层网络的构建和使用 一、实验目的 &#xff08;1&#xff09;了解网络层级、子网、动态地址、网关代理等概念并进行应用。 &#xff08;2&#xff09;了解OpenStack项目以及相关组件。 &#xff08;3&#xff09;了解 Neutron 二层网络的构建和使用。 二、实验原…

如何确定vtk .h文件所在的库名字

问题 例如使用class vtkSTLReader,头文件包含#include <vtkSTLReader.h>,库使用哪个&#xff1f; 解决 1.在vs2019解决方案资源管理器中搜索vtkSTLReader.cxx&#xff0c;显示project为IOGeometry即为库名 2.在所在的CMakeLists.txt追加库名 3.在cmake重新Configure、G…

JWT中的Token

1.JWT是什么&#xff1f; jwt&#xff08;json web token的缩写&#xff09;是一个开放标准&#xff08;rfc7519&#xff09;&#xff0c;它定义了一种紧凑的、自包含的方式&#xff0c;用于在各方之间以json对象安全地传输信息&#xff0c;此信息可以验证和信任&#xff0c;因…

如果大模型有信仰,那一定是“规模法则”

规模&#xff08;scale&#xff09;是除去时间、空间之外另一个重要的维度。规模缩放&#xff08;Scaling&#xff09;的过程中隐藏着世界非线性本质奥秘背后的共性——规模法则。结合伯努瓦曼德布洛特 Benoit Mandelbrot的《大自然的分形几何》、杰弗里韦斯特 Geoffery West 的…