javascript
/**
* 通过 URL 下载文件(使用 a 标签)
* @param {string} url - 文件地址
* @param {string} [fileName] - 文件名
*/
export function downloadUrl(url, fileName) {
const a = document.createElement('a');
a.href = url;
if (fileName) {
a.download = fileName;
}
a.style.display = 'none';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
/**
* 文件下载
* @param {object} axiosInstance - axios实例
* @param {string} url
* @param {string} method - 请求方法,默认 'post'
* @param {object} data - 请求参数
* @param {string} [fileName] - 文件名(可选)
* @param {function} [onProgress] - 下载进度回调函数 (percent, loaded, total) => {}
* @returns {Promise<string>} 返回下载的文件名
*/
export function downloadFile(axiosInstance, url, method = 'post', data = {}, fileName, onProgress) {
return axiosInstance({
url,
method,
data,
responseType: 'blob',
onDownloadProgress: progressEvent => {
if (progressEvent.lengthComputable) {
const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100);
onProgress?.(percent, progressEvent.loaded, progressEvent.total);
}
}
}).then(response => {
// 拦截器处理后,blob 响应返回 { data, headers } 对象
let responseData = null;
let responseHeaders = {};
if (response && typeof response === 'object') {
if (response.data instanceof Blob) {
responseData = response.data;
responseHeaders = response.headers || {};
} else if (response instanceof Blob) {
// 如果直接返回 blob
responseData = response;
} else {
responseData = response.data;
responseHeaders = response.headers || {};
}
} else {
responseData = response;
}
// 获取文件名
let finalFileName = fileName;
const disposition = responseHeaders['content-disposition'];
if (!finalFileName && disposition) {
// 尝试从 content-disposition 获取文件名
const utf8Filename = disposition.match(/filename\*=UTF-8''(.+)/i);
if (utf8Filename?.[1]) {
finalFileName = decodeURIComponent(utf8Filename[1]);
} else {
const asciiFilename = disposition.match(/filename=["']?([^"']*)["']?/i);
if (asciiFilename?.[1]) {
try {
// 解码(可能是 UTF-8 URL 编码)
finalFileName = decodeURIComponent(asciiFilename[1]);
} catch {
// 解码失败, 使用原始值
finalFileName = asciiFilename[1];
}
}
}
}
// 兜底:文件名使用时间戳
if (!finalFileName) {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
finalFileName = `${year}${month}${day}${hours}${minutes}${seconds}`;
}
// 获取文件类型
const contentType = responseHeaders['content-type'] || '';
const isCSV = contentType.includes('csv');
const fileExtension = isCSV ? 'csv' : contentType;
// 如果文件名没有扩展名,添加扩展名
if (!finalFileName.includes('.')) {
finalFileName = `${finalFileName}.${fileExtension}`;
}
// 下载
const blob = responseData instanceof Blob ? responseData : new Blob([responseData], { type: contentType });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = finalFileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(a.href);
return finalFileName;
});
}