[JS]熟悉又陌生的Blob

526 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

你一定会遇到过的Blob

相信每个前端开发者在职业生涯中肯定都会遇到过Blob,但却很少有人真的了解过这个东西究竟是什么。今天我们就一起来聊聊Blob吧。

什么是Blob?

首先我们需要知道Blob是JavaScript提供的一离二进制最近的对象。JavaScript作为一门高级语言,甚至大部分的代码都运行在浏览器中,因此JavaScript开发者实际上是很少机会与原生文件或者二进制接触的。但在某些场景下我们又不得不对二进制文件做一些操作,因此JavaScript为我们提供了Blob这个类型。Blob就是专门处理文件操作的。

如何创建一个Blob?

既然我们要讲Blob,我们需要先得到一个Blob。其中最简单的方法就是new一个出来。

new Blob(array, options)
// 参数说明
// - array: 一个由ArrayBuffer, ArrayBufferView, Blob, string objects,或者混合了前面内容的objects组成的数组。
// - options(选填):
//       - type: 描述Blob的类型,如 'text/html'

举个例子,我们可以通过下面的代码得到一个HTML文本Blob。

const array = ['<a id="a"><b id="b">hey!</b></a>']; 
const blob = new Blob(array, {type : 'text/html'});

Blob属性

通过打印上面代码生成的Blob,我们可以看到Blob有什么属性。

const array = ['<a id="a"><b id="b">hey!</b></a>']; 
const blob = new Blob(array, {type : 'text/html'});
console.log(blob);

可以看到Blob有2个属性:

  • size: Blob的大小,注意这里的单位是bytes(字节)
  • type:Blob的类型

实际中我们更有可能这样得到Blob

前面我们讲了怎么可以主动创建Blob。但在实际工作中又前端开发者主动创建Blob的情况是很少的。我们更多时候是通过API获得Blob。

从文件中获得

既然Blob是描述文件二进制的对象,我们当然能从文件中获得Blob。在浏览器中我们有2种方式获得文件:

  1. 通过标签获得
  2. 通过用户拖放操作获得

相信有过这方面业务经验的朋友看到这里应该会发现,通过以上2种方式得到的内容其实跟前文讲的Blob并不完全相同。一般会得到下图的内容:

这里的FileList对象是File对象是集合,而File对象又是一个基于Blob类型。File会针对文件增加一些属性,如文件名称,大小和类型等:

从canvas转换

除了从文件中获得以外,还有一种不常见的方式。就是通过调用canvas元素的toBlob()方法。

HTMLCanvasElement.toBlob(callback, type, quality)

该方法有3个参数:

  • callback:转换后的回调
  • type(选填):转换类型,默认是image/png
  • quality(选填):转换质量,一个0-1的数字。1就是质量最好。

关于Blob的实用操作

说完了怎么可以得到Blob之后,我们再来看看拿到Blob之后,我们可以做什么?

URL.createObjectURL(blob)

我们先来看一个API,URL.createObjectURL(blob)可以把一个Blob转成一个URL。观察下面的例子:

      const array = ['<a id="a"><b id="b">hey!</b></a>'];
      const blob = new Blob(array, { type: "text/html" });
      const url = URL.createObjectURL(blob);
      console.log(url);

打印内容为:

很明显可以看到这个URL用的是一个blob开头的协议,严格来说这个并不是一个真的协议,他是一个只能用在浏览器内部的引用地址。因此,不同于base64,这个url不会因为文件越大而越长。有了这个blob url之后我们就可以实现接下来的操作了。

提供下载URL

虽然blob url只能在浏览器内部使用,但通过a标签我们可以作出一个主动下载的动作,让用户把blob像文件一样下载到本地。

<a id="h">点此进行下载</a>
<script>
  const array = ['<a id="a"><b id="b">hey!</b></a>'];
  const blob = new Blob(array, { type: "text/html" });
  const url = URL.createObjectURL(blob);
  var a = document.getElementById("h");
  a.download = "helloworld.html";
  a.href = url;
</script>

加载文件

通过input标签或者拖拉得到的File对象,我们是无法直接使用的。但通过把Blob转成url的形式,可以让img标签加载图片。

<img src="" id="img" />
<input type="file" id="file" />
<script>
  const $file = document.querySelector("#file");
  const $img = document.querySelector("#img");
  $file.addEventListener("change", () => {
    const file = $file.files[0];
    const url = URL.createObjectURL(file);
    $img.src = url;
    console.log(url);
  });
</script>

文件分片上传

这个是跟服务器相关的功能,在某些时候我们需要从客户端上传文件到服务器。但如果文件太大,在网络不稳定的情况下有可能出现上传出错的问题,

Blob类型有一个slice方法,跟数组的slice一样,可以把Blob切成多个片段。通过把一个文件Blob切成多个片段再逐一上传,这就是分片上传的原理。

具体做法如下:

  1. 先确定每个片段的大小(每次上传多大的内容)
  2. 判断出需要把文件切成几份
  3. 逐一上传
  4. 每次确保前一个上传成功,否则就重新上传当前片段
  5. 所以片段上传成功后,通知服务器合并文件。
// 获得上传片段
function getBlobChunk (blob,chunkSize){
  const BLOB_SIZE = blob.size;
  const chunkList = [];
  let start = 0;
  let end = chunkSize;
 
  while (start < BlOB_SIZE) {
    chunkList.push(blob.slice(start, end));
    start = end;
    end = start + chunkSize;
  }
  return chunkList;
}

const $file = document.querySelector("#file");
$file.addEventListener("change", () => {
    const file = $file.files[0];
    const chunkList = getBlobChunk(file, 1024 * 1024 * 2);
    let target=chunkList.pop();
    while(chunkList.length){
      try{
        upload(target);
        target=chunkList.pop();
      }catch(err){
        console.log('上传失败,开始重试');
      }
    }
});

当然上面只是一个简单的演示例子,在真实场景中可能还会涉及一些额外的前后端交互。如果前端需要先拿一个操作id,让后台知道客户端在上传什么以及把上传的片段放到同一个地方。上传完之后根据id通知后台可以合并文件了。当然这个需要大家根据业务,自行设计。

总结

今天我们了解了JavaScript的Blob对象,讲述了获得Blob的方法和途径,以及我们可以用Blob实现一些如本地下载,渲染文件以及分片上传的操作。希望对大家有所帮助。

如果觉得本文对你有一点帮助的话,可以给我点下赞噢~