公共Hooks封装之文件下载useDownloadFile
写在前面
对于经常需要开发企业管理后台的前端开发来说,必不可少的需要使用表格对于数据进行操作,在对于现有项目进行代码优化时,封装一些公共的 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(url);
} else {
window.webkitURL.revokeObjectURL(url);
}
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(url);
} else {
window.webkitURL.revokeObjectURL(url);
}
document.body.removeChild(aElement);
}
};
xhr.send();
} catch (e) {
console.error(e);
downloading = false;
infoModal && infoModal.destroy();
Modal.error({
title: "提示",
content: "下载发生异常,请重试",
});
}
};
return {
downloadFile,
};
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南