Blob对象

730 阅读6分钟

什么是Blob对象

Blob对象在MDN官方文档中是这样解释的:

Blob 对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作。

其实一开始看到这个解释的时候还是有点懵逼,不可变原始数据类文件对象。那么什么是类文件对象呢?

我们知道,我们平时开发用的对象是javascript对象,有很多属性和方法,供我们去调用和访问。那类文件对象,顾名思义,也就是类似于文件的对象。说简单点,也就是描述一个文件的对象,可以访问他的大小(Blob.size)、类型(Blob.type)等信息,还可以对该对象进行一些操作,比如说文件切片;

不难看出,它就是用来描述我们一个文件类型的实例对象;

创建Blob对象时发生了什么

相信很多小伙伴都会遇到一个面试题,当创建一个javascript对象时都发生了什么?是的,我们这里来研究一下当创建一个Blob对象时都发生了什么?

我们先写代码,首先我们在HTML文件中声明一个<input id="file" type="file">标签,在js中通过new Blob()实例化一个Blob对象:

new Blob([new Int32Array()], {type: 'text/html'})

这里我们创建了一个32位有符号的整型数组,类型时'text/html'

好下一步我们在浏览器的导航栏输入chrome://blob-internals/,查看一下浏览器内部Blob的存储情况:

image.png

我们就会看到这样一个blob示例,它是对我们当前创建的Blob对象的一个描述。

我们先来看一下Blob对象源码是如何实现的:

[Exposed=(Window,Worker), Serializable]
interface Blob {
  constructor(optional sequence<BlobPart> blobParts,
              optional BlobPropertyBag options = {});

  readonly attribute unsigned long long size;
  readonly attribute DOMString type;

  Blob slice(optional [Clamp] long long start,
            optional [Clamp] long long end,
            optional DOMString contentType);

  [NewObject] ReadableStream stream();
  [NewObject] Promise<USVString> text();
  [NewObject] Promise<ArrayBuffer> arrayBuffer();
};

enum EndingType { "transparent", "native" };

dictionary BlobPropertyBag {
  DOMString type = "";
  EndingType endings = "transparent";
};

typedef (BufferSource or Blob or USVString) BlobPart;

Blob()可以使用零个或多个参数调用构造函数 。Blob()调用构造函数时,调用时会经过以下步骤:

  1. 如果无调用,则返回一个Blob由 0 字节组成的新对象,size设置为 0,type设置为空字符串。

  2. bytes是处理给定的blob 部分blobParts的结果和options

  3. 如果参数的type成员options不是空字符串,请运行以下子步骤:

    1. 使t成为type字典成员。如果t包含 U+0020 到 U+007E 范围之外的任何字符,则将t设置为空字符串并从这些子步骤返回。
    2. t中的每个字符转换为小写ASCII。
  4. 返回一个Blob引用bytes的对象作为其关联的字节序列,并将其size设置为bytes的长度,并将其type设置为上述子步骤中的t值。

什么场景使用

同学们在开发的时候,通常会在请求application/octet-stream字节流类型数据时使用,或者当前端本地需要上传二进制文件时使用。

而且我们常用的File对象也是继承自Blob对象的,比较常见的场景就是在操作或者存储我们二进制文件时,转化为可序列化的javascript对象时使用;

基本属性和方法

接下来会简单介绍一下几个常用的API。

  const blob = new Blob(["掘金"], { type: 'text/html' });
  console.log(blob.size) // 6
  console.log(blob.type) // text/html

Blob.size

这是个只读属性,表示查看我们Blob对象中内容的大小,是以自己为单位的:

我们知道,在UTF8中我们一个汉字占三个字节,所以这里我们一个一共两个汉字,就是6个字节;

Blob.type

这也是个只读属性,表示当前我们存储的数据类型;其实也就是Blob构造函数的第二个对象参数中的type字段的value;

Blob.slice

这个是原型上的方法, 官方的说法是创建包含指定字节范围的新Blob对象。说白了就是把一个Blob对象切片成多个小的文件;

一共有三个参数(均为非必传):

  1. start:是一个number类型,表示会被拷贝到新的Blob对象中的其实字节,如果是负数,将会自后像前数,例如-100,就是倒数第100个字节,如果传入的长度大于目标Blob对象的字节长度,将会返回长度为0的空Blob对象;

  2. end:这个参数代表的是 Blob 的一个下标,这个下标-1的对应的字节将会是被拷贝进新的Blob 的最后一个字节。如果你传入了一个负数,那么这个偏移量将会从数据的末尾从后到前开始计算。举例来说, -10 将会是 Blob 的倒数第十个字节。它的默认值就是它的原始长度(size).

  3. 给新的 Blob 赋予一个新的文档类型。这将会把它的 type 属性设为被传入的值。它的默认值是一个空的字符串。

首先我先选择上传这样一张图片:

image.png

  const File = document.getElementById("file");

  const uploadHandle = e => {
    const fileName = e.target.files[0].name
    const type = e.target.files[0].type;
    const blob = new Blob([e.target.files[0]], {
      type
    });
    console.log(blob)
    console.log(blob.size) // 119498
    console.log(blob.type) // image/png
  };
  File.addEventListener('change', uploadHandle);

通过上面的代码会得到这样的结果:

image.png

同时在浏览器的Blob存储器里,也会得到这样一个对象:

image.png

具体使用,我们接着上面的代码写:

  const File = document.getElementById("file");

  const uploadHandle = e => {
    const fileName = e.target.files[0].name
    const type = e.target.files[0].type;
    const blob = new Blob([e.target.files[0]], {
      type
    });
    console.log(blob)
    console.log(blob.size) // 119498
    console.log(blob.type) // image/png
    const fileChunks = [];
    const size = 1024 * 3; // 切片大小
    const totalsize = blob.size; // 文件尺寸
    let start = 0; // 切片位置
    while (start <= totalsize) {
      const chunk = blob.slice(start, start + size, blob.type);
      fileChunks.push({
        hash: start,
        chunk
      });
      start += size;
    }
    console.log(fileChunks)
  };
  File.addEventListener('change', uploadHandle);

通过上面的方法,我们将每一片分为 1024 * 3 个字节,通过执行循环语句,每次将切片位置向后移动,最后在控制台会得到这样的结果;

image.png

这样的话,我们就会对大文件进行切片,形成一个队列,最常用的方式就是消费这个队列去并发上传文件;

Blob.stream()

Blob接口的stream() 方法返回一个ReadableStream对象,读取它将返回包含在Blob中的数据。

Blob.text()

该方法返回的是一个Promise对象,使用的是UTF-8格式编码,主要是读取一些字符串类型;

  const HTML = `<div>
  <div>
    <p>123</p>
  </div>
</div>`;
  
  const blob = new Blob([HTML], {
    type: 'text/html'
  });
  blob.text().then(res => console.log(res)) 
  /* 会打印出
    <div>
      <div>
        <p>123</p>
      </div>
    </div>
  */

Blob.arrayBuffer()

该方法同样返回一个Promise对象, Pormise决议结果返回的是代表内存之中的一段二进制数据,该对象会继承TypedArray对象可以,通过“视图”进行操作;

总结

本篇文章我们主要介绍了一下什么是Blob对象,其实就是一个描述文件的类文件对象。其次我们介绍了一下创建Blob对象时构造函数内都发生了什么。其次简单说了一下常用的应用场景,最后结合代码,介绍了Blob的一些API,希望通过本篇文章,你会对Blob有一个更深刻的认识。