
/** * 公共下载文件 * @param {string} requestService * @param {string} fileId - 文件id * @param {string} fileName - 文件名称 */export default function downloadFile( fileParams: string | number | Record<string, any>, fileName: string, fileUrl: string = downloadApi) { if (!fileParams) return ElMessage.error('暂无文件Id'); requestService( fileUrl, (typeof fileParams === 'string' || typeof fileParams === 'number') ? { id: +fileParams } : fileParams, 'blob' ).then((res: any) => { const disposition = res.headers['content-disposition']; const blob = new Blob([res.data]); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; const fileExtension = getFileExtensionFromContentDisposition(disposition) || getFileSuffix(disposition); let downloadName = fileName; // 仅当存在有效扩展名时进行校验 if (fileExtension) { const lastDotIndex = fileName.lastIndexOf('.'); // 检查是否含有效扩展名(点不在开头/结尾) const hasValidExtension = lastDotIndex > 0 && lastDotIndex < fileName.length - 1; if (hasValidExtension) { const fileNameExt = fileName.slice(lastDotIndex + 1).toLowerCase(); // 扩展名不一致时强制拼接 if (fileNameExt !== fileExtension.toLowerCase()) { downloadName = `${fileName}.${fileExtension}`; } } else { // 无扩展名时直接拼接 downloadName = `${fileName}.${fileExtension}`; } } a.download = downloadName; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(url); });}
/** * 从 Content-Disposition 头中提取文件后缀名 * @param {string} contentDisposition - Content-Disposition 头的值 * @returns {string} 文件后缀名,如果未找到则返回空字符串 */function getFileExtensionFromContentDisposition(contentDisposition: string) { if (!contentDisposition) return ''; // 1: 从 filename 字段提取 let filenameMatch = contentDisposition.match(/filename="([^"]+)"/); if (!filenameMatch) { // 2: 从 filename* 字段提取(UTF-8编码) filenameMatch = contentDisposition.match(/filename\*=UTF-8''([^;]+)/); } if (filenameMatch && filenameMatch[1]) { const filename = filenameMatch[1]; // 提取文件后缀(最后一个点之后的部分) const lastDotIndex = filename.lastIndexOf('.'); if (lastDotIndex !== -1 && lastDotIndex < filename.length - 1) { return filename.substring(lastDotIndex + 1).toLowerCase(); } } return '';}
// 取文件名小数点最后一个,作为后缀名function getFileSuffix(contentDisposition: string) { if (!contentDisposition) return ""; // 匹配最后一个点后面的内容,排除以点开头的隐藏文件 const match = contentDisposition.match(/^(?!\.)[^.]*\.([^.]+)$/); return match ? match[1].toLowerCase() : "";}