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.οnlοad=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
、onprogres
s等或者使用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