Cuuube / blog

blog on Mirror
1 stars 0 forks source link

[HTML5]Blob与FileReader #45

Open Cuuube opened 7 years ago

Cuuube commented 7 years ago

Blob与FileReader

背景

前几天看代码时,偶尔发现某站video元素的src里藏着没见过的科技。

就像这样:

<video src="blob://xxxxx.xxxx.xx/adasdasd"></video>

当时没时间,便加入长期观察。最近偶然看到类似字眼:“Blob对象”。便突然找到线索,搜索文档,写demo,差不多弄懂了这玩意是怎么形成,怎么运作的。

神秘url的生产者——URL.createObjectURL()

首先最奇怪的莫过是“blob://xxx”这个奇怪的,像是“http://”,“ftp://”这种,像一种协议

但却把他们复制到地址栏里却什么都没。

搜索文档后,看到了这个H5对象:blob

里面有这么一句:

var url = URL.createObjectURL(blob);
// 会产生一个类似blob:d3958f5c-0777-0845-9dcf-2cb28783acaf 这样的URL字符串

直译为生产带有对象的url

看来URL.createObjectURL()这个方法会直接生成带blob的这种url。

于是随手去控制台试了试:

我是图1

不行,并不能随便传入obj。仔细观察,上面例子有传入一个blob,这说明,blob类要出场了。

幕后主使——Blob对象

文档是这么说的:

要从其他非blob对象和数据构造一个Blob,请使用 Blob() 构造函数。要创建包含另一个blob数据的子集blob,请使用 slice()方法。要获取用户文件系统上文件的Blob对象,请参阅 File文档。

构造函数例子:

let personJson = `{"name":"Tom","age":15}`;
let blob = new Blob([personJson], {
    type: 'application/json'
});

文件内容放到第一个参数的数组中,第二个参数options中type指定类型。

上文我根据一段简单的json创建了个blob对象。此blob可以看做为文件系统级的.json文件。

如果我们打印出该blob对象,只能看到如下:

> Blob {size: 23, type: "application/json"}
>    size:23
>    type:"application/json"
>    __proto__:Blob

只能看到size和type,无法看到内部文件内容。因为内部文件内容已经不是js文本格式了,而是application/json文件格式。

当然,blob还能存储其他文件格式,如图片,视频,二进制数组等。在构造函数中指定正确的type即可。

(有些坑我还没踩,比如怎么把png的二进制传到blob构造函数里,用什么js对象当容器装png二进制。)

扒光Blob的英雄——FileReader

如果说Blob是一个隐藏在最深处的幕后黑手的话,FileReader就是闯进虎穴,将Blob拉到灯光下,扒了个干净。。。

好吧,其实就是一个阅读文件的接口。

FileReader可以读出blob的二进制文件,也能获得包含Blob文件信息的url(最开始说的那种)。

//基本使用, 比如要从上面blob变量还原出json:
let reader = new FileReader();
reader.onload = function (e) {
    console.log(e.target.result);
};
reader.readAsText(blob);

//> {"name":"Tom","age":15}

readAsText为将读取结果作为文本输出。

还有常用的为readAsDataURL,将读取结果作为包含Blob文件信息的url(最开始说的那种)。例子:

let reader = new FileReader();
let image = document.getElementById('img'); // 假如有个#img的元素
reader.onload = function (e) {
    let url = e.target.result;
    image.src = url;
}
reader.readAsDataURL(imageBlob); // 假如有一个包含图片文件的blob

这里用了onload方法,onload会在读取成功完成后,调用。

和它相似的还有onloadend,读取结束之后,不管成功或失败都会调用。

Blob 和 File的关系

都知道,在文件上传时,可以获得input[type="file"]元素的file对象。

let input = document.querySelector('input[type="file"]');
input.onchange = function (e) {
    let file = this.files[0];
    // 操作file...
}

你如果console.log出来file,会看到,file对象包含文件名,文件大小等信息,但看不到二进制文件。

是不是很熟悉?对,File对象和Blob非常相似!

其实相似是正常的,因为他们是父子关系。如果在控制台里看file对象的__proto__,就能看到,File继承自Blob。

我是图2

所以我们平时在做图片文件上传的预览时,其实就是用FileReader扒开了File,将其图片内核赋给了img元素。所以我们才不用加载文件就能看到图。因为文件内容已经包含在了小小的URL里了啊!

最后,某站的video里,用的还有个MediaSource技术,专门处理流媒体文件的。有兴趣的可以直接戳进链接了解。

zhaoxieluoke commented 6 years ago

怎么得到真实的url呢?