前言
有幸在一个后台上传大文件的需求中了解到了Blob,其实真正接触Blob对象是在下载电影的时候,现在很多video标签的src路径是blob格式,当时只知道是流媒体,Blob还带着一层面纱,那么Blob到底是个啥,这里来浅谈一下~
一、Blob对象
Blob
对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成ReadableStream
来用于数据操作。
File
接口基于Blob
,继承了 blob 的功能并将其扩展以支持用户系统上的文件。
说人话:简单来讲,就是Blob可以将文件换成二进制格式,并对这个为二进制文件进行剪切和拼接操作,也是目前js唯一可以操作二进制的方式。
1、创建blob对象
var aBlob = new Blob( array, options );
参数:
array 是一个由ArrayBuffer
, ArrayBufferView
, Blob
, DOMString
等对象构成的 Array
,或者其他类似对象的混合体,它将会被放进 Blob
。DOMStrings 会被编码为 UTF-8。
options 是一个可选的BlobPropertyBag字典,它可能会指定如下两个属性:
- type,默认值为 "",它代表了将会被放入到 blob 中的数组内容的 MIME 类型。
- endings,默认值为"transparent",用于指定包含行结束符\n的字符串如何被写入。它是以下两个值中的一个:"native",代表行结束符会被更改为适合宿主操作系统文件系统的换行符,或者 "transparent",代表会保持 blob 中保存的结束符不变
举例:
var b1 = '<a id="a"><b id="b">hey!</b></a>'; // DOMString
var b2 = '123';
var aBlob = new Blob([b1], {type:'text/html'} );
console.log(aBlob);
//Blob {size: 35, type: 'text/html'}
实例方法:
Blob实例对象有以下方法:不再一一详述,大家感兴趣可以点击方法名去文档做了解
- slice():返回一个新的 Blob 对象,包含了源 Blob 对象中指定范围内的数据。
- stream():返回一个能读取 blob 内容的 ReadableStream。
- text():返回一个 Promise 对象且包含 blob 所有内容的 UTF-8 格式的 USVString。
- arrayBuffer():返回一个 Promise 对象且包含 blob 所有内容的二进制格式的 ArrayBuffer
2、创建blob URL
平时我们常见到的还是blob URL
Blob url 是指向存储在浏览器缓存或磁盘中的blob的一个引用。
//格式如:
blob:https://www.jianshu.com/u/685589ccbc2a
Blob URL可以通过URL.createObjectURL(blob)创建。在绝大部分场景下,我们可以像使用Http协议的URL一样,使用Blob URL。常见的场景有:作为文件的下载地址和作为图片资源地址。
举例:
给上传图片创建一个Blob URL
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<input id="J_input" type="file" onchange="handleFile(this)" />
<br/>
<img id="J_img" src="">
</body>
<script>
var img = document.getElementById('J_img');
function handleFile(e) {
var file = e.files[0];
var blob = URL.createObjectURL(file);
img.src = blob;
img.onload = function(e) {
URL.revokeObjectURL(this.src); // 释放createObjectURL创建的对象##
}
}
</script>
</html>
注:在每次调用 createObjectURL()
方法时,都会创建一个新的 URL 对象,即使你已经用相同的对象作为参数创建过。当不再需要这些 URL 对象时,每个对象必须通过调用 URL.revokeObjectURL()
方法来释放,清除占用内存。
浏览器在 document 卸载的时候,会自动释放它们,但是为了获得最佳性能和内存使用状况,你应该在安全的时机主动释放掉它们。
二、常用场景
当我们上传大文件时,传输速度过慢,如果中间断开链接,也会导致传输文件丢失,这时常用blob对象来进行分片上传
分片上传
主要是用Blob对象的slice方法将原文件剪切成碎片,然后依次将碎片上传。
1、slice方法
Blob.slice()
方法用于创建一个包含源Blob
的指定字节范围内的数据的新Blob
对象。
前边提到,Blob 对象是不可改变的。我们不能直接在一个 Blob 中更改数据,但是我们可以对一个 Blob 进行分割,从其中创建新的 Blob 对象,将它们混合到一个新的 Blob 中。这种行为类似于 JavaScript 字符串:我们无法更改字符串中的字符,但可以创建新的更正后的字符串。
var blob = instanceOfBlob.slice([start [, end [, contentType]]]};
参数:
-
start:
可选
- 这个参数代表
Blob
里的下标,表示第一个会被会被拷贝进新的 Blob的字节的起始位置。 - 如果你传入的是一个负数,那么这个偏移量将会从数据的末尾从后到前开始计算。举例来说,-10 将会是 Blob的倒数第十个字节。
- 它的默认值是 0,如果你传入的 start 的长度大于源 Blob 的长度,那么返回的将会是一个长度为 0 并且不包含任何数据的一个 Blob对象。
- 这个参数代表
-
end:
可选
- 这个参数代表
Blob
里的下标,这个下表-1的对应字节将会呗拷贝进新的Blob的最后一个字节 - 如果你传入一个负数,那么这个偏移量将会从数据的末尾从后往前开始计算。举例来说,-10 将会是 Blob的倒数第十个字节。
- 它的默认值是他的原始长度(size)。
- 这个参数代表
-
contentType:
可选
- 给新的Blob赋予一个新的文档类型。这将会把它的 type 属性设为被传入的值。它的默认值是 一个空的字符串。
举例
var data = "12345678";
var blob1 = new Blob([data]);
var blob2 = blob1.slice(0,4);
console.log(blob1); //输出:Blob {size: 8, type: ""}
console.log(blob2); //输出:Blob {size: 4, type: ""}
2、分片上传
前面已经说过,
File
接口基于Blob
,继承了 blob 的功能并将其扩展以支持用户系统上的文件。因此我们可以调用File的slice方法对大文件进行分片长传。代码如下:
注:file.slice()返回的是blob对象
function uploadFile(file) {
var chunkSize = 1024 * 1024; // 每片1M大小
var totalSize = file.size;//文件总长度
var chunksNum = Math.ceil(totalSize / chunkSize); //分片总数
var index = 0; // 分片的索引标记,记录是第几片
var reader = new FileReader();
reader.onload = function(e) {
var xhr = new XMLHttpRequest();
xhr.open("POST","http://xxxx/upload?fileName="+file.name);
xhr.overrideMimeType("application/octet-stream");
xhr.onreadystatechange = function() {
if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
++index;
if(index === chunksNum) {
alert("上传完成");
} else if(offset === chunksNum-1){
blob = file.slice(index*chunkSize, totalSize); // 上传最后一片
reader.readAsBinaryString(blob);
} else {
blob = file.slice(index*chunkSize, (index+1)*chunkSize);
reader.readAsBinaryString(blob);
}
}else {
alert("上传出错");
}
}
if(xhr.sendAsBinary) {
xhr.sendAsBinary(e.target.result); // e.target.result是此次读取的分片二进制数据
} else {
xhr.send(e.target.result);
}
}
var blob = file.slice(index, chunkSize);
reader.readAsBinaryString(blob);//开始上传时读取第一片文件,第一次触发reader.onload
}
这个代码中用到了FileReader对象中的readAsBinaryString方法来读取blob文件片,并触发onload事件监听文件读取完成的时间节点,同时onload返回的 result
属性包含所读取文件原始二进制格式,也就是最终上传的文件内容。
注:
使用FileReader时,一定记住readAsBinaryString方法和onload成对使用,没有readAsBinaryString方法,则无法触发onload。