文章目录

    • 问题
    • 实现
      • 采用axios实现下载请求
      • 写一个进度下载对话框
      • 调用对话框
    • 参考链接

问题

上传下载是前端经常面临的两大需求,当文件比较大时,下载进度显示能提升用户体验。本文结合vue3介绍下载对话框的实现。当点击页面中下载按钮后,会呈现类似下面效果的对话框:

下载进度达到100%时,点击保存按钮即可保存文件。

主要原理:axios下载文件时,文件数据作为blob对象先放到内存中,然后可以对这个blob对象做各种操作。

实现

采用axios实现下载请求

进度显示主要利用了axios的onProgress()重载方法。

import Axios, { AxiosInstance, AxiosRequestConfig, ResponseType } from "axios";const downloadConfig: AxiosRequestConfig = {baseURL,timeout: 100000,responseType: "blob",headers: {'Content-Type': 'application/octet-stream'}}private static axiosInstance: AxiosInstance = Axios.create(defaultConfig);private static axiosDownloadInstance: AxiosInstance = Axios.create(downloadConfig);public download<T, P>(url: string, filename: string, onProgress: DownloadProgress, onCompleted: DownloadCompleted): Promise<P> {const config = {method: 'get',onDownloadProgress: (evt)=>onProgress(evt.loaded, evt.total),url} as PureHttpRequestConfig;return new Promise((resolve, reject) => {PureHttp.axiosDownloadInstance.request(config).then((response: any) => {onCompleted(response.data);resolve(response);}).catch(error => {reject(error);});});}

写一个进度下载对话框

download_dialog.vue的实现:

<template><el-dialog v-model="visible" :title="title" :append-to-body=true width="50%"><el-progress :text-inside="true" :stroke-width="26" :percentage="percent" /><template #footer><span><el-button type="primary" @click="cancel()">取消</el-button><el-button :type="type" @click="doSave()" :disabled="disabled">保存</el-button></span></template></el-dialog></template><script lang="ts" setup>import {ref, shallowRef, watch, onMounted, getCurrentInstance} from "vue"import {ElDialog, ElForm, ElFormItem, ElInput, ElButton, ElProgress} from "element-plus";import {closeDialog} from "/@/components/dialog"import { http } from "/@/utils/http";const props = defineProps<{downloadUrl:string, filename: string}>()const visible = ref<boolean>(true)const type = ref('info')const disabled = ref<boolean>(true)const title = ref(`${props.filename}》下载`)const percent = ref<number>(0)const blobData = ref()onMounted(() => {http.download(props.downloadUrl, props.filename, onProgress, onCompleted)})const onProgress = (loaded, total) => {percent.value = parseInt(loaded * 100 / total)if (percent.value == 100) {disabled.value = falsetype.value = 'success'}}const onCompleted = (blob) => {blobData.value = blob}const cancel = () => {visible.value = false}const doSave = () => { var urlObject = window.URL || window.webkitURL || windowconst url = urlObject.createObjectURL(new Blob([blobData.value], {type: 'application/octet-stream',}));let saveLink = document.createElement('a')saveLink.href = urlsaveLink.download = props.filename + '.pdf'saveLink.click()URL.revokeObjectURL(url);visible.value = false}</script>

调用对话框

利用vue3的h()和render()函数绘制对话框:

import {Component, h, render, shallowRef} from "vue";import DownloadDialog from "./download_dialog.vue"/** * 开启一个下载对话框 * @param downloadUrl :下载文件的链接 * @param filename :保存文件的名称 * @returns*/export function openDownloadDialog(downloadUrl, filename) {const vnode = h(DownloadDialog, {downloadUrl, filename})vnode.appContext = nullconst container = document.createElement('div')render(vnode, container)const instance = vnode.componentconst vm = instance.proxyreturn vm}

测试:

const downloadPDF = async (bookId, bookName) => {openDownloadDialog("/api/ebook/download/pdf/" + bookId, bookName)}

参考链接

  • https://muhimasri.com/blogs/how-to-save-files-in-javascript/