根据业务需求和后端返回的数据选择对应的方法。想要先预览,不立即下载选择异步方法;没有特殊要求一般都用a链接直接下载文件。
同步:使用a链接,直接下载文件
function handleExport() {
//获取表单对象,antd的用法
let value = getForm().getFieldsValue();
// 调用api获取文档流
exportEntryReport(value).then((res) => {
const blobData = new Blob([res], {
type: 'application/vnd.ms-excel',
});
//创建一个空的 a 标签
const downloadA = document.createElement('a');
// 将 blob 文件转化为 url 格式并赋值给a标签的 href 属性
downloadA.href = URL.createObjectURL(blobData);
// 给a标签设置下载链接并命名
downloadA.setAttribute('download', '进场统计表.xlsx');
// 点击事件触发 href
downloadA.click();
});
}
异步:获取code,用code换取下载地址,文件页面单独打开
1、封装下载的方法
// download.ts
//打开新窗口并访问指定的 URL的方法
//target:目标窗口的上下文或名称,默认为 "__blank",即在新标签页打开。
//noopener:是否要添加 noopener 属性,默认为 true。noopener 是一个安全性设置,用于防止新打开的页面操作原始页面的 window 对象。
//noreferrer:是否要添加 noreferrer 属性,默认为 true。noreferrer 是另一个安全性设置,用于防止新打开的页面发送 Referer 头信息,从而隐藏来源地址。
export function openWindow(
url: string,
opt?: { target?: string; noopener?: boolean; noreferrer?: boolean },
) {
const { target = '__blank', noopener = true, noreferrer = true } = opt || {};
const feature: string[] = [];
//通过传入可选的参数来设置是否添加 noopener 和 noreferrer 等窗口特性
noopener && feature.push('noopener=yes');
noreferrer && feature.push('noreferrer=yes');
window.open(url, target, feature.join(','));
}
//url:要下载的文件的 URL。
//target:目标窗口的上下文,默认为 '_blank',即在新标签页中下载。
//fileName:要保存的文件名,默认为 undefined。
export function downloadByUrl({
url,
target = '_blank',
fileName,
}: {
url: string;
target?: string;
fileName?: string;
}): boolean {
//1、判断用户浏览器的类型
//通过 navigator.userAgent 来判断是否为 Chrome 浏览器或 Safari 浏览器
const isChrome = window.navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
const isSafari = window.navigator.userAgent.toLowerCase().indexOf('safari') > -1;
//如果是 iOS 设备返回 false,因为 iOS 不支持直接下载
if (/(iP)/g.test(window.navigator.userAgent)) {
console.error('Your browser does not support download!');
return false;
}
//对于 Chrome 和 Safari 浏览器,创建一个 <a> 元素
if (isChrome || isSafari) {
const link = document.createElement('a');
link.href = url;
link.target = target;
//检查浏览器是否支持下载属性来决定是否设置下载文件的名称。如果 link.download 存在(即浏览器支持下载属性),则将其设置为传入的 fileName 值或从 URL 中提取的文件名
if (link.download !== undefined) {
link.download = fileName || url.substring(url.lastIndexOf('/') + 1, url.length);
}
//如果浏览器支持 document.createEvent,则创建一个 MouseEvents 事件,将其分派给 <a> 元素,触发文件下载
if (document.createEvent) {
const e = document.createEvent('MouseEvents');
e.initEvent('click', true, true);
link.dispatchEvent(e);
return true;
}
}
//如果浏览器不是 Chrome 或 Safari,或者不支持 document.createEvent,则判断 URL 是否已经包含查询参数 '?'。如果没有查询参数,则在 URL 后面添加 '?download',作为下载标识
if (url.indexOf('?') === -1) {
url += '?download';
}
//最后,调用之前提到的 openWindow 函数,打开带有下载标识的 URL,并传入目标窗口的上下文。然后返回 true,表示下载操作已经开始
openWindow(url, { target });
return true;
}
2、页面中使用
先创建一个变量用来声明计时器。
先获取code,用code调后端接口获取address字段,如果是200说明没获取到下载链接,使用setTimeout计时器三秒后再次调接口查找,这样轮询直到拿到。
获取到地址后,使用上面封装好的方法实现文件预览和下载。
//获取code
let timer: NodeJS.Timeout;
function handleExport() {
let params = {
...getForm().getFieldsValue(),
};
exportVehicles(params).then((res) => {
if (res.code == 200) {
const paramCode = res.data?.code;
message.success('正在导出');
handlePollingExportValue(paramCode);
} else {
message.error(res.message);
}
});
}
//用code换取下载地址
function handlePollingExportValue(paramCode) {
let params = {
code: paramCode,
};
isSyncing.value = true;
downloadVehicles(params).then(({ data, code }) => {
const address = data?.address;
if (code == 200) {
if (address == 200) {
timer = setTimeout(() => {
handlePollingExportValue(paramCode);
}, 3000);
} else {
if (address) {
downloadByUrl({
url: address,
target: '_self',
});
} else {
message.error('找不到错误地址');
}
isSyncing.value = false;
message.success('导出文件下载完成');
clearTimeout(timer);
}
} else {
isSyncing.value = false;
}
});
}
在 TypeScript 中,let timer: NodeJS.Timeout; 表示声明了一个名为 timer 的变量,并指定其类型为 NodeJS.Timeout。
NodeJS.Timeout 是 Node.js 提供的一个类型,它表示一个定时器的句柄。当使用 setTimeout 或 setInterval 函数创建定时器时,返回的值就是 NodeJS.Timeout 类型,可以将其赋值给 timer 变量。通过这样的声明,可以在后续的代码中使用 timer 变量来管理定时器,例如取消定时器或者清除已经存在的定时器。