这里自己封装了一个关于文件下载的类。开发者只需要通过实例一个下载对象,就可以进行单文件、多文件下载,并且可以监控文件下载进度,以及进行下载取消。
1.前期准备
因为涉及到多文件下载,这里需要基于jszip插件。可在文章顶部点击下载并在代码中引入。
2.代码
// 下载各类型文件
export class FileDownLoad {
constructor(path, process) {
if (Array.isArray(path)) {
if (path.length > 1) {
this.isMult = true
this.path = path
} else {
this.path = path[0]
this.isMult = false
}
} else {
this.path = path;
this.isMult = false
}
this.controller = new AbortController();
this.getProcess = process
}
//下载文件
downloadPaper() {
if (this.isMult) {
this.downloadMult()
} else {
this.downloadSingle()
}
}
//多文件压缩zip后下载 需引入多文件压缩插件
async downloadMult() {
const zip = new JSZip();
const {
signal
} = this.controller;
for (let i = 0; i < this.path.length; i++) {
// 获取文件名称
let fileName = this.getFileName(this.path[i]);
let fileType = this.getFileType(this.path[i]);
try {
const response = await fetch(this.path[i], {
method: "get",
mode: "cors",
signal
})
//设置下载进度
const data = await this.setProcess(response, fileName);
zip.file(fileName, new Blob(data, {
type: fileType == "pdf" ? "application/pdf" : fileType == "word" ?
"application/msword" : fileType == "excel" ? "application/vnd.ms-excel" : ""
}));
} catch (error) {
//是否是用户中止下载操作
if (error.name === 'AbortError') {
console.error('中止下载')
} else {
console.error('下载出错了')
}
}
}
zip.generateAsync({
type: "blob"
}).then((content) => {
let url = window.URL.createObjectURL(content);
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", this.getFileName(this.path[0]) + '等文件.zip');
document.body.appendChild(link);
link.click();
link.remove();
});
}
//单个文件下载
downloadSingle() {
// 获取文件类型
let type = this.getFileType(this.path);
// 获取文件名称
let fileName = this.getFileName(this.path);
if (type == 'img') {
this.downloadImg(this.path).then((res) => {
// 创建标签
const link = document.createElement("a");
link.href = res;
// a 标签设置 download 属性,直接下载图片
link.setAttribute("download", fileName + '.png');
document.body.appendChild(link);
link.click();
link.remove();
}).catch((err) => {
console.log(err);
})
} else {
this.downloadFile({
url: this.path,
type: type,
name: fileName
})
}
}
getFileType(url) {
if (url) {
// 先用斜杠截取
let tempArr1 = url.split('/');
// 获取到截取结果的最后一位,即文件名称
let fileName = tempArr1[tempArr1.length - 1];
// 再用点截取
let tempArr2 = fileName.split('.');
// 得到文件类型
let type = tempArr2.at(-1);
let myType = ''
if (type == 'jpg' || type == 'jpeg' || type == 'png') {
myType = 'img'
} else if (type == 'pdf') {
myType = 'pdf'
} else if (type == 'doc' || type == 'doxc') {
myType = 'word'
} else if (type == 'xlsx' || type == 'xls') {
myType = 'excel'
}
return myType;
} else {
return '';
}
}
getFileName(url) {
if (url) {
// 先用斜杠截取
let tempArr1 = url.split('/');
// 获取到截取结果的最后一位,即文件名称
let fileName = tempArr1[tempArr1.length - 1];
return fileName;
} else {
return '';
}
}
downloadImg(url) {
return new Promise((resolve, reject) => {
// 重绘图片
let image = new Image();
image.onload = function () {
let canvas = document.createElement('canvas');
canvas.width = this.naturalWidth;
canvas.height = this.naturalHeight;
canvas.getContext('2d').drawImage(image, 0, 0);
let result = canvas.toDataURL('image/png')
resolve(result);
};
// 跨域获取当前图片
image.setAttribute("crossOrigin", 'Anonymous');
image.src = url
image.onerror = () => {
reject(new Error('urlToBase64 error'));
};
})
}
setProcess(response, name) {
try {
return new Promise(async resolve => {
const reader = response.body.getReader();
const contentLength = +response.headers.get('Content-Length');
let receiveLength = 0;
let chunks = [];
while (true) {
const {
done,
value
} = await reader.read();
if (done) {
break
}
chunks.push(value);
receiveLength += value.length;
this.getProcess(receiveLength, contentLength, name)
}
resolve(chunks)
})
} catch (error) {
if (error.name === 'AbortError') {
console.error('中止下载')
} else {
console.error('下载出错了')
}
}
}
downloadFile(data) {
const {
signal
} = this.controller;
try {
fetch(data.url, {
method: "get",
mode: "cors",
signal
})
.then((response) => {
//下载进度设置
this.setProcess(response, data.name).then((res) => {
const downloadUrl = window.URL.createObjectURL(
//new Blob() 对后端返回文件流类型处理
new Blob(res, {
type: data.type == "pdf" ? "application/pdf" : data.type == "word" ?
"application/msword" : data.type == "excel" ? "application/vnd.ms-excel" : ""
})
);
//word文档为msword,pdf文档为pdf
const link = document.createElement("a");
link.href = downloadUrl;
link.setAttribute("download", data.name);
document.body.appendChild(link);
link.click();
link.remove();
})
})
} catch (error) {
if (error.name === 'AbortError') {
console.error('中止下载')
} else {
console.error('下载出错了')
}
}
}
}
3.使用
通过new FileDownLoad实例一个对象fileDownLoad 。
paths:可直接访问的文件地址,字符串或数组类型。代码会判断:单个文件时触发单文件下载;多个文件时会生成一个压缩包并下载。
getProcess:文件下载进度的回调,返回当前接收数据量、总数据量、文件名称;可以通过这些参数进行下载进度的展示。
fileDownLoad = new FileDownLoad(paths, getProcess);
fileDownLoad.downloadPaper();
//获取下载进度
const getProcess = function (receive, total, name) {
fileProcessShow.value = receive !== total;
filePercent.value = ((receive / total) * 100).toFixed(0);
fileTotal.value = (total / (1024 * 1024)).toFixed(2) + "M";
fileName.value = name;
};
4.取消下载
调用实例对象 fileDownLoad.controller.abort 方法取消下载。
const stopDownloadClick = function () {
fileDownLoad.controller.abort();
fileDownLoad = null;
fileProcessShow.value = false;
fileTotal.value = 0;
filePercent.value = 0;
};