背景
遇到的需求是大文件上传途中刷新页面之后,不重新选择文件,继续上传。
思路
- 所在上传途中将进度保存在localstorage当中,将文件保存在indexedDB中,因indexedDB存在兼容问题,此处只在谷歌浏览器中使用。我使用的是GoDB操作indexedDB(https://godb-js.github.io/li97cu.html) 2.刷新页面之后,在onMounted中判断文件是否存在,存在直接调用上传方法
html部分代码
<el-upload
action="#"
:auto-upload="false"
:on-change="handleChange"
:show-file-list="false"
multiple
>
<span> 上传课件 </span>
</el-upload>
数据定义
interface fileObj {
name: string;
type: string;
size: string;
file: any;
percentage: number;
abortCheckpoint: any;
client: any;
}
// 定义indexedDB数据结构,除谷歌浏览器外,不启用
let fileTable: any = null;
if (browserIsHide()) {
// indexedDB表结构
const schema = {
fileTable: {
fileName: {
type: String,
unique: true, // 指定 name 字段在表里唯一
},
},
};
const fileDB = new GoDB("fileDB", schema);
fileTable = fileDB.table("fileTable");
}
const fileList = ref<fileObj[]>([]);
const newFileList = ref<fileObj[]>([]);
获取文件
const handleChange = async (file: UploadFile) => {
fileList.value.push({
name: file.name.substring(0, file.name.lastIndexOf(".")),
type: file.name.substring(file.name.lastIndexOf(".") + 1).toLowerCase(),
size: (file.size / (1024 * 1024)).toFixed(2) + "MB",
file,
percentage: 0,
abortCheckpoint: false,
client: null,
});
fileList.value.forEach((e: fileObj) => {
if (e.name.length > 13) {
e.name = `${e.name.substring(0, 12)}...`;
}
});
};
onMounted,页面加载处理
onMounted(() => {
// 断点续传 刷新之后从localstorage获取进度,从indexeddb获取文件
// 判断是否为谷歌浏览器
if (browserIsHide()) {
let localFileList = localStorage.getItem("newFileList");
if (localFileList) {
const newFileList = JSON.parse(localFileList);
if (newFileList.length > 0) {
newFileList.forEach(async (localItem: fileObj) => {
await fileTable
.find((item: any) => {
return item.fileName === localItem.file.name;
})
.then((data: { file: any }) => (localItem.file.raw = data.file));
});
fileList.value = [...newFileList];
if (fileList.value.length > 0) {
dialogVisible.value = true;
uploadForEach(fileList.value);
}
}
}
}
});
多文件上传,想要控制单个文件的暂停取消等操作,要给每个文件添加client方法
// 获取oss信息 生成client 赋值给每个文件 item为获取的file文件
const getOss = async (item: fileObj) => {
//此处项目种获取ossDemo可以根据实际情况进行更改
let res: any = await ApiGetOss()
let isPass = {
pass: true,
};
if (res.code == 0) {
//给item添加client方法
item.client = new OSS({
accessKeyId: res.data.accessKeyId,
accessKeySecret: res.data.accessKeySecret,
stsToken: res.data.securityToken,
bucket: res.data.bucket,
});
} else {
isPass = { ...res, pass: false };
}
return isPass;
}
获取上传进度,文件存储localstorage和indexedDB
const options = (item: fileObj, fileList: fileObj[]) => {
return {
checkpoint: item.abortCheckpoint,
progress: (p: number, cpt: any, res: any) => {
item.percentage = Number((p * 100).toFixed(2));
item.abortCheckpoint = cpt;
// 除谷歌浏览器外,不启用
if (browserIsHide()) {
// 筛选文件后,实时存储上传进度
newFileList.value = fileList.filter((item: fileObj) => {
return item.percentage < 100;
});
localStorage.setItem(
"newFileList",
JSON.stringify([...newFileList.value])
);
// 存储上传文件
newFileList.value.forEach(async (newItem: fileObj) => {
await fileTable
.find((item: any) => {
return item.fileName === newItem.file.name;
})
.then(async (data: any) => {
if (!data) {
await fileTable.add({
file: item.file.raw,
fileName: item.file.name,
});
}
});
});
}
},
};
};
上传到oss
const ossUpload = async (item: fileObj, fileList: fileObj[]) => {
//文件名uuid处理。后缀小写处理
let fileName = `${item.type}/${uuidv4()}.${item.type.toLowerCase()}`;
let isPass = {
pass: true,
filePath: "",
};
try {
//oss上传
let res: any = await item.client.multipartUpload(
fileName,
item.file.raw,
options(item, fileList)
);
console.log(res);
if (res.res.requestUrls.length > 0) {
let filePath = res.res.requestUrls[0].split("?")[0];
console.log(filePath);
isPass.filePath = filePath;
}
} catch (e: any) {
//上传失败处理
isPass = {
...e,
pass: false,
filePath: "",
};
}
//上传成功返回filepath
return isPass;
};
循环上传文件
const uploadForEach = (fileList: fileObj[]) => {
fileList.forEach(async (item: fileObj) => {
// 获取oss信息
const getOssRes: any = await getOss(item);
if (!getOssRes.pass) {
//获取失败,错误处理
return;
}
// oss上传
const ossUploadRes: any = await ossUpload(item, fileList);
// oss上传成功之后删除indexedDB数据
if (ossUploadRes.pass) {
// 除谷歌浏览器外,不启用
if (browserIsHide()) {
await fileTable
.find((itemDB: any) => {
return itemDB.fileName === item.file.name;
})
.then(async (data: any) => {
if (data) {
await fileTable.delete({ fileName: item.file.name });
}
});
}
}
});
};
调用上传方法
//获取到文件之后,根据业务需求进行调用上传方法
uploadForEach(fileList)
第一次写文档,有不足之处请指正,感谢!!!!!