动态文件名下载实现方案
硬编码文件名的弊端
硬编码文件名在实际项目中存在明显缺陷:
- 无法处理动态生成的文件名(如包含时间戳或服务器节点名)
- 后端更了解导出数据的业务语义,适合由后端命名
- 后端生成的唯一序列号可避免本地文件重名冲突
拦截器信息丢失问题
如果是已经开发完成的项目,通常Axios拦截器通常会直接返回res.data,导致响应头中的Content-Disposition字段丢失。解决方案是引入某个变量skipInterceptor配置,允许特定接口跳过拦截器处理。
核心实现步骤
- 通常需要对拦截器做一些改造,比如:
请求封装改造
javascript
service.interceptors.response.use(
async (response) => {
const config = response.config;
if (config.skipInterceptor) {
return response;
}
const { data } = response;
if (response.request.responseType === 'blob' || response.request.responseType === 'arraybuffer') {
return data;
}
return data;
}
);
使用示例:
文件名解析工具
javascript
function getRawFileName(header) {
if (!header) return 'download';
const match = header.match(/filename="?([^"]+)"?/);
const rawName = match ? match[1] : 'download';
try {
return decodeURIComponent(rawName);
} catch (e) {
return rawName;
}
}
业务层调用处理
javascript
const handleExport = async (payload) => {
try {
const res = await exportApiLogList(payload);
if (res.data.type === 'application/json') {
const text = await res.data.text();
const errorData = JSON.parse(text);
Message.error(errorData.msg || '导出失败');
return;
}
const disposition = res.headers['content-disposition'] || '';
const fileName = getRawFileName(disposition);
const blob = new Blob([res.data], { type: res.data.type });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
a.remove();
} catch (err) {
console.error('下载流程异常', err);
}
};
注意事项:主要需要看的内容
跨域Headers限制
后端需在响应头中显式暴露Content-Disposition:
Access-Control-Expose-Headers: Content-Disposition
本地环境调试限制
由于浏览器策略和开发环境代理的影响,本地服务可能无法获取文件名。建议在真实测试环境或生产环境下验证。
内存管理优化
使用URL.createObjectURL创建的链接会占用内存,需手动调用window.URL.revokeObjectURL(url)释放资源。
错误处理机制
必须检查res.data.type是否为application/json,避免用户下载到错误信息文件。