/** * 前端下载工具: * 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 { 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) }