vue3多文件oss断点续传,刷新页面之后不重新选取文件直接进行上传

1,228 阅读1分钟

背景

遇到的需求是大文件上传途中刷新页面之后,不重新选择文件,继续上传。

思路

  1. 所在上传途中将进度保存在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)

第一次写文档,有不足之处请指正,感谢!!!!!