// 引入所需的依赖项
import Axios from '@/utils/request'; // 替换为你的axios导入路径
import { ElLoading } from 'element-plus'; // 引入element-plus组件库中的loading组件
import FileSaver from 'file-saver';
// 分片大小设置
const CHUNK_SIZE = 52428800;
/**
* 下载方法
*
* @param url 下载的URL地址
* @param params 参数对象
* @param filename 文件名
*/
async function download(url: string, params: any, filename: string, contentType?: string) {
// 初始化loading状态
const loading = ElLoading.service({
text: '正在下载...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.5)',
});
try {
// 使用 Axios 发送请求
const response = await Axios.request<any>({
url: url,
method: 'post',
responseType: 'arraybuffer',
data: params,
headers: {
'Content-Type': contentType || 'application/octet-stream',
'Access-Control-Allow-Origin': '*' // 允许跨域请求
}
});
let realFilename = filename; // 默认为前端提供的文件名
// 解析响应头中的Content-Disposition信息,得到原文件名
if (response.headers['content-disposition']) {
let headerFilename = decodeURI(response.headers['content-disposition'].split(';')[1].trim();
if(headerFilename.startsWith('filename=')){
realFilename = headerFilename.replace('filename=', '');
}
}
let content;
// 检查响应体的数据类型,如果是ArrayBuffer则转成blob
if (response.data instanceof ArrayBuffer) {
content = new Blob([new Uint8Array(response.data)], { type: contentType });
let fileSize = content.size;
// 如果文件超过500M启用分片下载
if (fileSize > CHUNK_SIZE) {
let loaded = 0;
let chunks = Math.ceil(fileSize / CHUNK_SIZE);
for (let chunkId = 0; chunkId < chunks; chunkId++) {
const chunkStart = chunkId * CHUNK_SIZE;
const chunkEnd = Math.min(chunkId * CHUNK_SIZE + CHUNK_SIZE - 1, fileSize - 1);
const blobPart = content.slice(chunkStart, chunkEnd);
const partName = `${realFilename}-${chunkId}.${filename.split('.')[1]}`;
// 保存分片文件
await FileSaver.saveAs(blobPart, partName);
// 更新进度
loaded += CHUNK_SIZE;
loading.message = `${loaded}/${fileSize} (${(loaded / fileSize * 100).toFixed(2)}%)`;
if (chunkId === chunks - 1) {
break;
}
// 暂停一段时间以避免网络拥堵
setTimeout(() => {}, 500);
}
} else {
// 如果未超出大小限制,则直接下载
await FileSaver.saveAs(content, realFilename);
}
} else if (response.data instanceof Blob) {
const file = await response.data.arrayBuffer();
saveAs(new Blob([file]), filename);
} else {
throw new Error("未知文件类型");
}
} catch (e) {
console.error(e);
} finally {
// 关闭loading状态提示
loading.close();
}
}
我在示例代码中明确写出定义CHUNK_SIZE
的地方,你可以根据实际情况自己设定它的值,如CHUNK_SIZE=2097152
(大约2MB),它代表着每一片要下载的大小,如果你想要更快的下载速度,可以适当增大此数值,反之减小则会提高对服务器的压力。
优点:
- 合理设置响应头信息后,能够处理不同格式的文件,例如csv,txt,jpg等等。
- 适应各种类型的返回值,包括arrayBuffer或Blob。
- 提供进度指示,能够让用户了解文件下载的进度情况。
- 使用回调函数对下载操作进行监控。
缺点:
- 缺少断点续传功能,若网络故障导致文件下载中断,之前下载过的部分不会被保留,从零开始重新下载。
- 当客户端内存不足以一次性承载整个文件时,可能会影响用户体验。
- 不能利用HTTP分段请求(Range request),避免并发下载导致网络拥塞。
- 对于非常大的文件,可能存在性能问题,尤其是对超大型文件分片处理不够细致。
- 服务器如果没有特别指定 Access-Control-Allow-Origin 头,就不能下载来自其他网站的资源。