公共Hooks封装文件下载useDownloadFile实例详解
JasonSubmara 人气:0引言
对于经常需要开发企业管理后台的前端开发来说,必不可少的需要使用表格对于数据进行操作,在对于现有项目进行代码优化时,封装一些公共的Hooks.
本篇文章为useDownloadFile.js
基于个人项目环境进行封装的Hooks,仅以本文介绍封装Hooks思想心得,故相关代码可能不适用他人
项目环境
Vue3.x + Ant Design Vue3.x + Vite3.x
对于企业管理后台中常见的各类资源文件(图片、文档、音视频等),下载保存本地则是再正常不过的需求了,为保证统一性和避免每个单页面文件内重复书写冗余代码,封装此方法
封装前提:各方法对比
方法 | 操作原理 | 优点 | 缺点 |
---|---|---|---|
form 表单 | 动态生成一个表单,利用表单提交的功能来实现文件的下载 | 兼容性好,不会出现URL长度限制问题 | 无法知道下载的进度,用户体验交互差 无法直接下载浏览器可直接预览的文件类型 |
window.open / location.href | 打开新标签页访问下载资源 | 简单粗暴 | 会出现URL长度限制问题 无法知道下载的进度,用户体验交互差 无法直接下载浏览器可直接预览的文件类型 需要注意url编码问题 不能添加header,也就不能进行鉴权 |
<a /> download 属性 | 利用a标签原生访问属性,附加新增的download属性,使用浏览器进行下载 | 简单粗暴且可下载正常预览文件 | 不能下载跨域地址文件 IE/Edge内兼容问题 无法鉴权 |
Blob 对象 | 发请求获取二进制数据,转化为Blob对象,利用URL.createObjectUrl生成url地址,赋值在a标签的href属性上,结合download进行下载 | 能解决不能直接下载浏览器可浏览的文件 可以鉴权 | IE10以下不可用 Safari使用情况可能有问题 |
综上并结合实际项目,最后使用Blob对象进行封装下载文件方法
封装分解:下载核心代码
xhr.onloadend = function (e) { if (e.target.status === 200 || e.target.status === 304) { const aElement = document.createElement('a'); const blob = e.target.response; const url = window.URL.createObjectURL(blob); aElement.style.display = 'none'; aElement.href = url; aElement.download = `${options.fileName}.${fileType}`; document.body.appendChild(aElement); aElement.click(); if (window.URL) { window.URL.revokeObjectURL(blob); } else { window.webkitURL.revokeObjectURL(blob); } document.body.removeChild(aElement); } }; xhr.send();
封装分解:用户体验设计
- 下载过程中,配合项目使用的Ant Design Vue框架,可以加强用户感知文件下载进度,
- 为防止用户暴力点击,重复触发下载的问题,使用Loading Flag标识,
- 下载失败后,提示用户,重新下载
createVNode('div', {}, ['文件下载过程中请勿关闭当前页面']), createVNode('div', { className: 'mt-2' }, [`当前下载进度 ${progress.value}%`]), catch (e) { console.error(e); downloading = false; infoModal && infoModal.destroy(); Modal.error({ title: '提示', content: '下载发生异常,请重试', }); }
useDownloadFile.js完整代码
import { createVNode, ref, onBeforeUnmount } from 'vue'; import { Modal } from 'ant-design-vue'; export function useDownloadFile() { let xhr = null; let downloading = false; // 限制同一文件同时触发多次下载 let infoModal; onBeforeUnmount(() => { xhr && xhr.abort(); }); const downloadFile = options => { try { if (downloading || !options.url || !options.fileName) return; downloading = true; options.url = options.url.replace('http://', 'https://'); const progress = ref(0); const fileType = options.url.split('.').pop(); xhr = new XMLHttpRequest(); xhr.responseType = 'blob'; xhr.open('get', options.url, true); infoModal = Modal.info({ title: '文件下载', okText: '取消下载', content: () => { return createVNode('div', {}, [ createVNode('div', {}, ['文件下载过程中请勿关闭当前页面']), createVNode('div', { className: 'mt-2' }, [`当前下载进度 ${progress.value}%`]), ]); }, onOk() { xhr.abort(); return Promise.resolve(); }, }); xhr.onprogress = function (e) { progress.value = Math.floor((e.loaded / e.total) * 100); if (progress.value === 100) { downloading = false; infoModal.destroy(); } }; xhr.onloadend = function (e) { if (e.target.status === 200 || e.target.status === 304) { const aElement = document.createElement('a'); const blob = e.target.response; const url = window.URL.createObjectURL(blob); aElement.style.display = 'none'; aElement.href = url; aElement.download = `${options.fileName}.${fileType}`; document.body.appendChild(aElement); aElement.click(); if (window.URL) { window.URL.revokeObjectURL(blob); } else { window.webkitURL.revokeObjectURL(blob); } document.body.removeChild(aElement); } }; xhr.send(); } catch (e) { console.error(e); downloading = false; infoModal && infoModal.destroy(); Modal.error({ title: '提示', content: '下载发生异常,请重试', }); } }; return { downloadFile, }; }
加载全部内容