文件切片上传-仅学习记录

一、目的:

本文仅记录一次实践尝试,仅前端调试成功,后端未反馈问题,不过后来接口废弃了。

二、前期博客浏览参考:

本文链接:文件分片上传【前端】_分片上传原理-CSDN博客

本文链接:前端大文件切片上传,断点续传、秒传等解决方案,vue中使用实例,react同理_前端文件切片_刘伟坤的博客-CSDN博客

原文链接:前端大文件上传优化方案——分片上传 - 知乎 

三、原理:

分片上传的整个流程大致如下:
(1) 将需要上传的文件按照一定的分割规则,分割成相同大小的数据块;

(2) 向服务端发送上传请求,上传时携带完整文件的唯一标识(建议使用MD5生成编码值),服务端判断该文件是否存在,不存在是返回同意上传信息。

(3) 接收到服务端同意上传后,按照一定的策略(串行或并行)发送各个分片数据块,每个分片携带分片信息(分片总数,分片索引,分片唯一索引,分片数据等),采用并行可实现分片秒传的功能,采用串行更好实现断点续传的功能。

(4) 发送完成后,服务端根据判断数据上传是否完整,如果完整,则进行数据块合成得到原始文件。

四、手动实践:

前端后端可能都是新手菜-_-

1.  后端提供一个文件上传接口

  • 文件相关属性 传参:
    file fileName chunkNumber totalChunks
    文件 文件名 当前序号 分片总数
  • 返回值:文件路径 filePath
  • 接口逻辑:最后一个切片上传成功后,会返回服务器上存储的文件路径

2. 页面准备

    
        选择文件
    
    确定
      chunkSize: 4 * 1024 * 1024, // 4MB
      chunks: [], //切片存放数组
      totalChunks: 0, //切片总数量
    // 上传至页面
    uploadFile(file) {
      this.fileForm.fileName = file.file.name;
      this.fileForm.file = file.file;
    },
    // 上传至服务器
    confirmFile() {
      this.$refs.fileForm.validate((valid) => {
        if (valid) {
          let loading = this.$loading({
            lock: true,
            text: "上传中,请稍后...",
            spinner: "el-icon-loading",
            background: "rgba(0, 0, 0, 0.5)",
          });
            
          // 切片上传 + 文件业务
          
        } else {
          return false;
        }
      });
    },

3. 核心:切割

(方式1)使用Blob.splice(),但是最后打印的chunks,我看不懂,不知道是不是对应的file文件。

    const fileSize = this.fileForm.file.size;
    this.chunks = []; //不能省
    this.totalChunks = Math.ceil(fileSize / this.chunkSize);
    for (let i = 0; i < this.totalChunks; i++) {
      let start = i * this.chunkSize;
      let end = Math.min(fileSize, start + this.chunkSize);
      let blob = this.fileForm.file.slice(start, end);
      this.chunks.push(blob);
    }
    // console.log('切割后文件数组',this.chunks);

(方式2)使用fileReader ,但是没有实践,前期准备的时候 搜罗的代码

// const fileReader = new FileReader();
// fileReader.onload = () => {
//   const chunk = fileReader.result;
//   // TODO: Upload chunk to server
//   chunkIndex++;
//   if (chunkIndex < numChunks) {
//     loadNext();
//   }
// }

// const loadNext = () => {
//   const start = chunkIndex * chunkSize;
//   const end = Math.min(start + chunkSize, fileSize);
//   fileReader.readAsArrayBuffer(this.selectedFile.slice(start, end));
// }

 4.切片上传

为适应接口,采用promise包裹分片上传的接口,最终使用peomise.all获取最后一个切片的返回值

    // 分片上传
    let p = [];
    this.chunks.forEach((item, index) => {
      p[index] = new Promise((resolve, reject) => {
        let fd = new FormData();
        fd.append("file", item);
        fd.append("fileName", this.fileForm.fileName);
        fd.append("chunkNumber", index + 1);
        fd.append("totalChunks", this.totalChunks);
        spliceFileUpload(fd).then( 
            ({ data: { code, result: { filePath } } }) => {
            if (code == 200) {
              resolve(filePath);
            } else {
              reject("上传失败");
            }
          }
        );
      });
    });

5. 上传成功调用业务接口

    // 上传成功则导入
    Promise.all(p)
      .then((paths) => {
        //调用业务接口(paths.at(-1))
      })
      .catch((err) => {
        // 文件上传失败 业务失败
        console.log(err);
        this.$message.error(err);
        loading.close();
      });

你可能感兴趣的:(Vue,vue.js,javascript)