前端如何操作文件对象进行切片

535 阅读2分钟

类文件对象 File 与 Blob

对文件的处理离不开File对象。File提供了文件的相关信息,它继承于Blob对象,并添加了name(文件名)、lastModified(最后一次修改的时间戳)等属性。 我们可以看一下从 <input type="file"> 获取到的文件是什么样的(点击这里, 复制以下代码,在线调试):

<!doctype html>
<html>
<body>
    <input type="file" onchange="showFile(this)">
    <script>
        function showFile(input) {
          let file = input.files[0];
          console.log(file)
        }
    </script>
</body>
</html>

在这里插入图片描述

读取文件

你无法直接看到文件里的数据, 但是可以通过API,按照想要的格式进行读取。FileReader 是一个对象,其唯一目的是从 Blob(因此也从 File)对象中读取数据。 读取方法:

读取方法读取格式
readAsArrayBuffer(blob)读取成 ArrayBuffer 格式
readAsText(blob, [encoding])读取成 给定编码(默认为 utf-8 编码)的文本字符串
readAsDataURL(blob)读取成 base64
abort()取消操作

读取事件:

事件过程
loadstart开始加载。
progress在读取过程中出现。
load读取完成,没有 error。
abort调用了 abort()。
error出现 error。
loadend读取完成,无论成功还是失败。

读取结果:

结果内容
reader.result读取结果
reader.error错误信息

示例:读取图片 获取base64 data url

<!doctype html>
<body>
<input type="file" onchange="readFile(this)">

<script>
    function readFile(input) {
      let file = input.files[0];

      let reader = new FileReader();

      reader.readAsDataURL(file);

      reader.onload = function() {
        console.log(reader.result);
      };

      reader.onerror = function() {
        console.log(reader.error);
      };

    }
</script>
</body>

// // expected output: data:image/png;base64,iVBORw0KGgoAAA......(太长了)

截取文件

Blob 对象是不可改变的。 我们无法直接在 Blob 中更改数据,但 Blob 提供了slice方法, 返回一个新的 Blob 对象,包含了源 Blob 对象中指定范围内的数据。 所以我们可以通过 slice 截取 Blob 的多个部分,从这些部分创建新的 Blob 对象,将它们组成新的 Blob,等。而 File 接口没有定义任何方法,但是它从 Blob 接口继承了 slice,所以是通用的。

<!doctype html>
<html>
<body>
    <input type="file" onchange="readFile(this)">

    <script>
        function readFile(input) {
          let file = input.files[0];
          console.log(file)
          // 从中截取1M数据
          const slicer = file.slice(0, 1024*1024)
          console.log(slicer)
        }
    </script>
</body>
</html>

使用Generator 函数进行文件切片

const MB = 1024 * 1024;

// 切片方法
function* slice(file) {
    let i = 0;
    let start = 0;
    while (true) {
    	// 这里规定 1MB/片 最后一片 < 1MB
        const end = start + MB;
        const blob = file.slice(start, end);
        if (blob.size === 0) break;
        yield { chunk: ++i, blob };
        start = end;
    }
}

切片批量上传

文件分片上传一般需要以下参数:

  1. 上传id (一般是上传之前从后端先获取来的)
  2. 分片数据
  3. 当前片数
// 单个切片上传
async function upload(id, slice) {
    const { status, msg } = await this.axios.post(
        `/upload/file?id=${id}&chunk=${slice.chunk}`,
        slice.blob,
        {
            headers: { 'Content-Type': 'application/octet-stream' },
        },
    );
    if (status !== 200) throw new Error(msg);
}

// 切片批量上传
async function batchUpload(id, file) {
    const slicer = this.slice(file);
    for(const slice of slicer) {
    	console.log(slice);
        await this.upload(id, slice);
    }
}
  1. generator函数返回的是generator对象, 满足迭代器协议,可以使用 for of 进行遍历。
  2. 所以 slicer 中 其实包含所有的 { chunk, blob } 切片信息,我们将其逐一发送。

slice.png

参考资料