Blob介绍
Blob是对大数据块的不透明引用或者句柄。名字源于SQL数据库,表示“二进制大数据”(Binary Large Object)。在JavaScript中Blob通常表示二进制数据,但是不一定是大量数据。Blob是不透明的,我们可以对它执行的操作只有获取它的大小,MIME类型和将他切割成更小的Blob。——《JavaScript权威指南》
Blob 对象表示一个不可变、原始数据的类文件对象。Blob 表示的不一定是JavaScript原生格式的数据。——MDN
说人话就是Blob为一些JavaScript操作二进制数据的API提供了数据交换机制的支持。比如我们比较熟系的File接口就是基于Blob实现的,也是我们使用的Blob最多的方式,我们还可以借助Blob使用XMLHttpRequest从网络下载文件(二进制数据),并且Blob可以使用postMessage在窗口之间传递。
构造函数
var aBlob = new Blob( array, options );
返回一个新创建的 Blob对象,其内容由参数中给定的数组串联组成。
array是一个由ArrayBuffer,ArrayBufferView,Blob,DOMString等对象构成的Array,或者其他类似对象的混合体,它将会被放进Blob。DOMStrings会被编码为UTF-8。options是一个可选的BlobPropertyBag字典,它可能会指定如下两个属性:
a.type,默认值为"",它代表了将会被放入到blob中的数组内容的MIME类型。
b.endings,默认值为"transparent",用于指定包含行结束符\n的字符串如何被写入。 它是以下两个值中的一个:"native",代表行结束符会被更改为适合宿主操作系统文件系统的换行符,或者"transparent",代表会保持blob中保存的结束符不变
构造一个html文件类型的Blob
var aFileParts = ['<a id="a"><b id="b">hey!</b></a>']; // 一个包含DOMString的数组
var oMyBlob = new Blob(aFileParts, {type : 'text/html'}); // 得到 blob
属性
Blob.size只读
Blob对象中所包含数据的大小(字节)。Blob.type只读
一个字符串,表明该Blob对象所包含数据的MIME类型。如果类型未知,则该值为空字符串。
方法
Blob.slice(start, end, contentType)
返回一个新的Blob对象,包含了源 Blob 对象中指定范围内的数据,start参数代表范围的开始下标,end代表复制范围的结束下标,contentType表示返回的Blob的文档类型。
let blob=new Blob(["这是保存到Blob中的文本数据"],{type:"application/text"})
let newBlob=blob.slice(0,5,"text/html");
Blob.stream()
返回一个能读取blob内容的 ReadableStream,使用 stream() 函数与其返回的ReadableStream对象,我们可以做如下的事:
- 调用方法
getReader(),在返回的stream上获取一个对象,通过ReadableStreamDefaultReader接口提 供的read()方法读取blob对象的方法。 - 调用返回
stream对象的pipeTo()方法将blob对象的数据传输到可写流。 - 调用返回
stream对象的tee()方法以准备可读流。该方法会返回一个数组,该数组包含两个新的ReadableStream对象,每个对象都会返回Blob的内容。 - 调用返回
stream对象的pipeThrough()方法,通过一个TransformStream对象或其它任意可读可写对传输流对象。
let blob=new Blob(["这是保存到Blob中的文本数据"],{type:"application/text"})
var stream = blob.stream();
let reader=stream.getReader()
reader.read().then(content=>/*处理读取到的数据*/console.log(content));
Blob.text()
返回一个promise且包含blob所有内容的UTF-8格式的 USVString。
let blob=new Blob(["这是保存到Blob中的文本数据"],{type:"application/text"})
var textPromise = blob.text();
blob.text().then(text => /* 执行的操作…… */console.log(text));
var text = await blob.text();
console.log(text)
本文后面要介绍另一种读取Blob的方法FileReader,两者都可以读取Blob的内容,区别如下:
Blob.text()返回的是一个promise对象,而FileReader.readAsText()是一个基于事件的 API。Blob.text()总是使用UTF-8进行编码,而FileReader.readAsText()可以使用不同编码方式,取决于 blob 的类型和一个指定的编码名称。
Blob.arrayBuffer()
返回一个promise且包含blob所有内容的二进制格式的 ArrayBuffer
var bufferPromise = blob.arrayBuffer();
blob.arrayBuffer().then(buffer => /* 处理 ArrayBuffer 数据的代码……*/);
var buffer = await blob.arrayBuffer();
FileReader.readAsArrayBuffer() 这个方法与之类似,但 arrayBuffer() 返回一个 promise 对象,而不是像 FileReader 一样返回一个基于事件的 API。
Blob URL
我们可以创建一个URL指向Blob,从而使得它可以使用。
我们可以通过URL.createObjectURL()创建一个Blob URL,兼容的方法:
let getBlobURL=(window.URL&&URL.createObjectURL)||(window.webkitURL&&webkitURL.createObjectURL)||window.createObjectURL;
传递一个Blob对象给createObjectURL()会返回一个URL,实际上是一个形如blob://xxx的字符串,blob协议之后的部分是一个简单的blob在浏览器中保存的引用。
下面是一个使用Blob URL实现显示拖放的图片文件的功能:
<!DOCTYPE html>
<html>
<head>
<title>Blob</title>
<style>
#dragTarget{
border: solid black 2px;
width: 200px;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
}
#dragTarget.active{
border: solid red 4px;
}
</style>
</head>
<body>
<div>
<div id="dragTarget">
将文件拖放到此处
</div>
</div>
<script>
//禁用浏览器默认打开事件
document.addEventListener('drop', function (e) {
e.preventDefault()
}, false)
document.addEventListener('dragover', function (e) {
e.preventDefault()
}, false)
let dragTarget=document.getElementById("dragTarget");
dragTarget.ondragenter=function(e){
let types=e.dataTransfer.types;
//判断是否是文件类型
if(!types||(types.contains&&types.contains("Files"))
||(types.indexOf&&types.indexOf("Files")!=-1)){
//触发高亮
dragTarget.classList.add('active');
dragTarget.textContent="松手显示图片"
return false;
}
}
dragTarget.ondragleave=function(){
dragTarget.classList.remove('active');
dragTarget.textContent="将文件拖放到此处"
}
dragTarget.ondragover=function(){return false};
dragTarget.ondrop=function(e){
let files=e.dataTransfer.files;
//遍历插入图片
Array.prototype.forEach.call(files,file => {
let type=file.type;
//判断是否是图片类型
if(type.indexOf('image/')!=-1){
let img=document.createElement("img");
img.onload=function(){
this.width=100;
this.height=100;
document.body.append(this);
//要记得回收URL
URL.revokeObjectURL(file);
}
//创建URL并赋值给img的src属性
img.src=URL.createObjectURL(file);
}
});
//完成操作,移除高亮样式
dragTarget.classList.remove("active");
dragTarget.textContent="将文件拖放到此处"
return false;
}
</script>
</body>
</html>
文件下载
我们结合Blob URL和XMLHttpRequest可以实现文件从服务器下载功能。
let req=new XMLHttpRequest();
req.open("GET",url);
req.responseType="blob";
req.onload=function(){
let resBlob=this.response;
resBlob.type = "application/octet-stream";
//使用Blob创建URL
let url=URL.createObjectURL(resBlob);
let a=document.createElement("a");
//将url赋值给a标签的href属性
a.href=url;
//下载的文件的文件名
a.download="要下载的文件名称.扩展名";
//触发click事件执行下载
a.click();
//要记得回收URL
URL.revokeObjectURL(url);
}
req.send(null);
我们也可以本地构造一个html文件类型的Blob并下载到本地
let aFileParts = ['<a id="a"><b id="b">hey!</b></a>']; // 一个包含DOMString的数组
let oMyBlob = new Blob(aFileParts, {type : 'text/html'}); // 得到 blob
let a=document.createElement("a");
let url=URL.createObjectURL(oMyBlob);
//将url赋值给a标签的href属性
a.href=url;
//下载的文件的文件名
a.download="要下载的文件名称.html";
//触发click事件执行下载
a.click();
//要记得回收URL
URL.revokeObjectURL(url);
同理构造一个文本类型Blob并下载到本地
let resBlob=new Blob(["这是要放入Blob中的字符串数据"],{type:"application/text"});
let a=document.createElement("a");
let url=URL.createObjectURL(resBlob);
//将url赋值给a标签的href属性
a.href=url;
//下载的文件的文件名
a.download="要下载的文件名称.txt";
//触发click事件执行下载
a.click();
//要记得回收URL
URL.revokeObjectURL(url);
要记得回收BlobURL对象,因为这个对象不会被自动回收。
FileReader读取Blob
我们可以使用FileReader对象来读取Blob中的字节或者字符。FileReader的api都是异步的,但是他在Worker中有对应的同步版本FileReaderSync。
要使用FileReader首先要创建它的实例,然后为它添加事件处理函数onload、onerror、onprogress等或者使用addEventListenter监听事件。FileReader对象还会触发loadstart、loadend、abort事件。
FileReader有4个读取Blob内容的方法:readAsText()、readAsArrayBuffer()、readAsDataURL()和readAsBinaryString()。以上方法都接收Blob对象作为第一个参数,readAsText()方法还接收第二个可选参数指定文本的编码方式,默认是ASCII和UTF-8。
- 使用readAsText()方法的例子:
<div>
<input type="file" onchange="readFile(this.files[0])" />
<div id="content"></div>
</div>
<script>
function readFile(file){
//创建FileReader对象
let reader=new FileReader();
//读取Blob,因为File对象为一个Blob对象的封装
reader.readAsText(file);
//添加onload事件处理函数
reader.onload=function(){
let text=reader.result;//获得读取结果
let content=document.getElementById("content");
content.innerHtml=text;
//将结果写入到界面
content.appendChild(document.createTextNode(text));
}
//添加onerror事件处理函数
reader.onerror=function(error){
console.log(error);
}
}
</script>
- 使用readAsArrayBuffer()方法读取的例子:
<div>
<input type="file" onchange="typeFile(this.files[0])" />
</div>
<script>
function typeFile(file){
//创建FileReader对象
let reader=new FileReader();
let typeArea=file.slice(0,4);//读取文件开头部分(前四个字节)
//读取Blob,因为File对象为一个Blob对象的封装
reader.readAsArrayBuffer(typeArea);
//添加onload事件处理函数
reader.onload=function(){
let buffer=this.result;
let view=new DataView(buffer);
let type=view.getUint32(0,false);//以高位优先字节顺读取4个字节
switch(type){
case 0x89504E47:
file.m_type="image/png";
break;
case 0x47494638:
file.m_type="image/gif";
break;
case 0x25504446:
file.m_type="application/pdf";
break;
case 0x504b0304:
file.m_type="application/zip";
break;
}
console.log(file.name,file.m_type);
}
//添加onerror事件处理函数
reader.onerror=function(error){
console.log(error);
}
}
</script>
最后
这篇文章是本人在开发中有一个点击按钮下载excel文件的需求,在查阅实现方式的过程中发现需要结合Blob来实现下载。所以就总结了一下Blob的基本概念和常用使用方法,并列举了应用案例。如有错误,还望指正。如果感觉还可以,欢迎点赞!
参考文献:
JavaScript权威指南
MDN web docs