64 lines
1.6 KiB
TypeScript
64 lines
1.6 KiB
TypeScript
/**
|
||
* 前端下载工具:
|
||
* 1) 先调用 validate=1 做权限/文件可用性校验
|
||
* 2) 再通过浏览器原生附件下载,避免 blob: 协议兼容性问题
|
||
*/
|
||
|
||
export interface DownloadValidationError {
|
||
code?: string
|
||
error?: string
|
||
details?: string
|
||
}
|
||
|
||
/**
|
||
* 为下载 URL 追加校验参数
|
||
*/
|
||
export function buildValidateDownloadUrl(downloadUrl: string): string {
|
||
const url = new URL(downloadUrl, window.location.origin)
|
||
url.searchParams.set('validate', '1')
|
||
return `${url.pathname}${url.search}${url.hash}`
|
||
}
|
||
|
||
/**
|
||
* 触发浏览器原生下载(使用服务端 Content-Disposition)
|
||
*/
|
||
export function triggerAttachmentDownload(downloadUrl: string): void {
|
||
const link = document.createElement('a')
|
||
link.href = downloadUrl
|
||
link.style.display = 'none'
|
||
document.body.appendChild(link)
|
||
link.click()
|
||
|
||
// 延迟移除,确保浏览器已经接管下载任务
|
||
setTimeout(() => {
|
||
link.remove()
|
||
}, 500)
|
||
}
|
||
|
||
/**
|
||
* 先校验再下载,失败时抛出后端错误对象
|
||
*/
|
||
export async function validateAndTriggerDownload(downloadUrl: string): Promise<void> {
|
||
const validateUrl = buildValidateDownloadUrl(downloadUrl)
|
||
const response = await fetch(validateUrl, {
|
||
method: 'GET',
|
||
cache: 'no-store',
|
||
credentials: 'include',
|
||
})
|
||
|
||
if (!response.ok) {
|
||
const fallbackError: DownloadValidationError = {
|
||
code: 'UNKNOWN_ERROR',
|
||
error: '下载失败',
|
||
}
|
||
|
||
let errorData: DownloadValidationError = fallbackError
|
||
try {
|
||
errorData = await response.json()
|
||
} catch {}
|
||
throw errorData
|
||
}
|
||
|
||
triggerAttachmentDownload(downloadUrl)
|
||
}
|