likes
comments
collection
share

实现大文件切片上传

作者站长头像
站长
· 阅读数 43

前言 🍊🍊

在上一篇文章中我们讲解了如何进行图片的一个压缩处理,里面涉及到很多与文件相关的知识,我们在实际项目开发中可能会用到文件上传操作,今天就来学习一下如何实现文件的切片上传。

文件是如何上传的?

首先我们要知道一般上传文件有两种方式:

  • 基于FormData的格式来进行文件的上传
  • 基于Base64的格式来进行上传

这两种方式上传各有优缺点:

Base64上传

  • 优点:
  • 简单易用:将文件转换为Base64字符串,然后将其作为文本数据发送,不需要额外的表单数据。 * 跨平台兼容:Base64是一种文本格式,可以在任何支持文本传输的平台上使用。 * 不需要额外的文件流处理:Base64数据可以直接包含在请求的主体中,不需要使用文件流进行处理。
  • 缺点:
  • base64上传的最大缺点就是他会是原本的文件的体积增大约1/3,这对于大文件上传尤其不利

  • 由于文件的体积增大,上传的效率自然也就变低

FormData上传

  • 优点:
  • 支持多文件上传:通过FormData可以同时上传多个文件,方便批量上传。
  • 文件流处理:FormData可以使用文件流的方式上传文件,适用于大型文件或流式传输的场景。
  • 可以携带其他表单数据:FormData可以同时携带其他表单数据,方便与文件相关的其他数据一起传输。
  • 缺点:
  • 使用稍复杂:相比Base64上传,使用FormData上传需要创建一个FormData对象,然后将文件和其他表单数据附加到该对象上。
  • 需要服务器端处理:使用FormData上传需要服务器端进行相应的解析和处理,提取上传的文件和表单数据。

了解一些文件相关的知识:🎮🎮

我们经常能够看到一些文件相关的知识例如 FlieReader,File,Blob等等相关的知识,其实可以用一张图来说明:

实现大文件切片上传

Blob

Blob 全称为 binary large object ,即二进制大对象。blob对象本质上是js中的一个对象,里面可以储存大量的二进制编码格式的数据。

blob对象上面一般会有两个属性:

size:Blob对象中所包含数据的大小(字节);

type:字符串,认为该Blob对象所包含的 MIME 类型。如果类型未知,则为空字符串。

同时我们应该要注意到Blob 对象内置了 slice() 方法用来将 blob 对象分片

start:设置切片的起点,即切片开始位置。默认值为 0,这意味着切片应该从第一个字节开始;

end:设置切片的结束点,会对该位置之前的数据进行切片。默认值为blob.size; 这样我们就可以通过slice方法来完成对blob数据的切片操作,切片之后的数据还会返回一个blob类型的数据。

File类型

Flie类型其实是特殊类型的Blob类型

当我们点击input中选择文件之后会返回一个FlieList对象

也可以通过拖拽操作生成的 DataTransfer 对象;

FileReader对象

使用FileReader对象的目的就是因为Blob中的数据是无法读取的,因此我们要通过FileReader中的方法来转换为我们想要读取的数据类型

  • readAsArrayBuffer(file):按字节读取文件内容,结果用ArrayBuffer对象表示
  • readAsBinaryString(file):按字节读取文件内容,结果为文件的二进制串
  • readAsDataURL(file):读取文件内容,结果用data:url的字符串形式表示
  • readAsText(file,encoding):按字符读取文件内容,结果用字符串形式表示

在知道这些基础的知识之后我们就可以尽心大文件的切片上传操作

大文件的切片上传 🍯🍯

首先大家肯定会有疑问:什么是切片上传?

故名思意就是将一些大的文件切成一份一份的,来进行分开的上传操作,我们可以自定义切片的大小,然后后端在拿到数据之后在拼接起来。将其组装成为一个数据。

由于代码稍微有一点多,因此我们将其拆分成及部分来看

((doc) => {
  const selectFile = doc.querySelector("#selectFile");
  const progress = doc.querySelector("#progress");
  const uploadFile = doc.querySelector("#uploadFile");
  // 绑定点击事件
  function BindEvent() {
    selectFile.addEventListener("change", selectFuc);
    uploadFile.addEventListener("click", uploadSlice);
  }
   ....
   BindEvent();
  })(document)
  • 首先我们写一个立即执行函数,将一些初始化操作先完成,我们首先获取上传文件的input框,以及进度条和上传按钮的DOM元素。
  • 然后我们给元素绑定点击事件,单独将这部分抽离出来,是整体看起来更加的规范。
const UPLOAD_TYPE = {
  "image/png": "png",
  "image/jpg": "jpg",
  "image/jpeg": "jpeg",
  "video/mp4": "mp4",
};
 function selectFuc(e) {
    const File = e.target.files[0];
    console.log(File);
    if (!UPLOAD_TYPE[File.type]) {
      alert("格式不合要求");
      this.value = "";
      return;
    }
  • 然后我们完成校验函数的封装,当然这里我们只是进行了一个简单的封装,对文件的类型进行一个简单的校验。如果想要上传的文件按不符合要求,那么就弹出弹框提示,并且将input中的value赋值为空。
  • 这里我们可以通过e.target.file[0]来获取上传的元素
  • 我们这里将一些我们允许上传的文件单独写成了一个对象,这也是模块化的体现。
  // 上传函数
  async function uploadSlice() {
      //获取input选择上传的文件
    const file = selectFile.files[0];
    if (!file) {
      alert("上传内容步能为空");
      return;
    }
    const { name, type, size } = file;
    //  定义切片的大小
    const chunckSize = 64 * 1024; // 当前文件上传的大小
    let currentSize = 0;
    progress.max = size;
    while (currentSize < size) {
      // 切片切出来的元素
      const FileChunk = file.slice(currentSize, currentSize + chunckSize);
      console.log(FileChunk);
      const Form = createFormData({ name, size, type, FileChunk });
      await axios.get("http://localhost:3000/upload", Form);
      currentSize += FileChunk.size;
      progress.value = currentSize;
    }
  }

然后我们就要封装最重要的函数:切片上传函数

  • 首先我们首先判断是否选择了要上传的元素,如果没有选择要上传的元素,则弹出弹框并且return。
  • 然后我们通过解构将file中的常用属性解构出来
  • 紧接着我们设置切片的大小,这个大小可以自己选择,然后还要定义一个变量,保存的是当前的已经上传的元素的大小,初始值为0
  • 我们这里给进度条的DOM元素设置max为元素的大小,这与之后的progress的value值要进行配合,这样我们可以看清楚上传的进度
  • 然后创建Form元素,等会同样这里我们也封装了一个函数,等一会我们在展开说
  • 然后我们就要使用silce来进行切片,返回的是被切成小段的元素,有趣的是,如果将这一段通过FileReader中的方法转换为base64的格式,同样可以显示出来图片,只不过是被裁掉了一截。思考一下,根据这个我们可以实现什么?
  • 然后我们通过axios给后端发送请求,注意这里我的后端就是通过express简单的搭了一个,感兴趣的同学也可以搞一个,但是这里我们只是学习一下思路,这个不是重点,我就不展开来讲。
  • 然后每次发送完请求之后给currentSize加上切片上传的尺寸,表明当前已经完成的部分,然后给进度条的vaue赋值为currentSize。

封装创建FormData函数:

// 限制文件上传的类型
  // 创建FormData
  function createFormData({ name, type, size, currentSize }) {
    const Form = new FormData();
    Form.append("name", name);
    Form.append("type", type);
    Form.append("size", size);
    Form.append("currentSize", currentSize);
    return Form;
  }

这部分其实比较好理解,就是单独封装了一个函数,用来创建FormData,然后通过append讲这些属性追加进去,最后返回通过axios发送给后端。

总结:🤙🤙

通过这次学习,我们掌握了文件相关的知识,了解了文件的传输形式(FormData,base64),并且我们学习了如何进行大文件的切片上传,使自己有很多收获。

转载自:https://juejin.cn/post/7240020551405928485
评论
请登录