这几天在开发过程中,遇到了下载excel文件问题,其中服务端返回二进制文件流,需要前端自己对二进制文件流进行转换,用到了new Blob()
方式,便上网查阅相关资料。
Blob
全称:Binary Large Object
(二进制大型对象)
Blob 对象
是一个前端的一个专门用于支持文件操作
的二进制对象
,表示一个二进制文件
的数据内容
,表示一个不可变
、原始数据
的类文件对象。通常用来读写文件,比如一个图片文件的内容就可以通过 Blob 对象读写。Blob 对象。Blob 表示的不一定是JavaScript原生格式的数据。
而在前端工程中,我们通常在下面方式获得Blob对象:
(图片源于知乎@澎湖湾)
浏览器原生提供Blob() 构造函数
,用来生成实例
。Blob 的内容由参数数组中给出的值的串联组成。
new Blob(array[, options])
Blob构造函数接受两个参数
:
(必填)
:是一个包含实际数据的数组
,数组的成员可以是字符串
或二进制对象
,表示新生成的Blob实例对象的内容
。(成员可以是一个由 ArrayBuffer
, ArrayBufferView
, Blob
, DOMString
等对象构成的 Array ,或者其他类似对象的混合体,它将会被放进 Blob。DOMStrings会被编码为UTF-8)(可选)
:是一个配置对象
,表示数据的MIME 类型。options包含两个属性:type
和 endings
,默认空字符串。这里介绍常用的属性 typelet new_blob = new Blob(['hello world'], {type : 'text/html'})
这里data :一个包含 DOMString 的数组
这里options :指定数据类型为 text/html
new_blob是一个blob实例
const obj = { hello: 'wang' };
const blob = new Blob([ JSON.stringify(obj) ], {type : 'application/json'})
这里data :一个包含 JSON数据 的数组
这里options :指定数据类型为 application/json
new_blob是一个blob实例
blob 对象也具有两个实例属性
:
对象的数据大小
,即文件的大小,单位为字节
对象所包含数据的MIME类型
。如果类型无法确定
,则返回空字符串
console.log(new Blob(['hello world'], {type : 'text/html'}))
//Blob {size: 11, type: "text/html"}
let obj = {name:'wang'};
let new_blob = new Blob([ JSON.stringify(obj) ], {type : 'application/json'})
console.log(new_blob)
//Blob {size: 15, type: "application/json"}
blob 对象可以通过slice方法得到一个新的 blob对象
const newBlob = oldBlob.slice([start [, end [, contentType]]])
slice 方法接收三个可选参数
:
start
和 end
都是数值
,表示截取的范围
contentType
指定截取的内容的 MIME 类型
。返回
一个新的 Blob对象
。var blob = new Blob(['hello world'], {type: 'text/plain'});
console.log(blob.size); //11
var newBlob = blob.slice(3, 7, 'text/plain');
console.log(newBlob.size); //4
在JS中,有两个构造函数:File
和 Blob
,而File继承
了所有Blob的属性。所以在我们看来,File对象可以看作一种特殊
的Blob对象,继承了 blob 的功能并将其扩展使其支持用户系统上的文件
。
通常情况下,在前端工程中,我们通常在下面方式获得File对象:
我们接触的多数关于 File 的操作都是读取,js也为我们提供了手动创建 File 对象的构造函数:
File(bits, name[, options])。
bits (必须)
:参数的类型必须是ArrayBuffer
,ArrayBufferView
,Blob
,或者 Array[string]
或者任何这些对象的组合
。这是 UTF-8 编码的文件内容。name(必须)
:参数的类型为字符串String
,表示文件名称
,或者文件路径
options(可选)
:参数的类型为对象
。表示选项对象
,包含文件的可选属性:type 和 lastModified
。可用的选项如下:type
------ string
, 表示将要放到文件中的内容的MIME类型
。默认值为 空字符串
lastModified
------ 数值
,表示文件最后修改时间的 Unix 时间戳
(毫秒)。默认值为 Date.now()
。var file = new File(['text1', 'text2'], 'test.txt', {type: 'text/plain'});
console.log('--file1--',file)
// File { lastModified: 1631434865697
// lastModifiedDate: Sun Sep 12 2021 16:21:05 GMT+0800 (中国标准时间) {}
// name: "test.txt"
// size: 10
// type: "text/plain"
// webkitRelativePath: ""
// }
根据已有的 blob 对象创建 File 对象:
var content1 = ['Hello world'];
var blob1 = new Blob(content1, {type: 'text/plain'});
console.log('--blob1--',blob1)
var file2 = new File([blob1], 'test.png', {type: 'image/png'});
console.log('--file2--',file2)
//--blob1-- △ Blob {size: 11, type: "text/plain"}
//--file2-- △ File {lastModified: 1631435132101
// lastModifiedDate: Sun Sep 12 2021 16:25:32 GMT+0800 (中国标准时间) {}
// name: "test.png"
// size: 11
// type: "image/png"
// webkitRelativePath: ""}
File 对象的实例内容不可见,但是有以下属性可以访问:
属性名称 | 读 / 写 | 描述 |
---|---|---|
name | 只读 | 返回文件的名称 ,由于安全原因,返回的值并不包含文件路径 |
type | 只读 | 返回 File 对象 ,表示文件的媒体类型(MIME) 。例如 PNG 图像是 “image/png” |
size | 只读 | 返回文件的大小 |
lastModified | 只读 | 返回number ,返回所引用文件最后修改日期 ,自 1970年1月1日0:00 以来的毫秒数 |
lastModifiedDate | 只读 | 返回Date ,返回当前文件的最后修改日期 ,如果无法获取到文件的最后修改日期,则使用当前日期来替代 |
webkitRelativePath | 只读 | 返回 File 相关的path 或 URL |
示例:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<div>
<input type="file" id='f' />
div>
<script>
document.getElementById('f').addEventListener('change', function(event){
console.log('-this.files-',this.files)
const file = this.files[0];
if (file) {
console.log('file.name',file.name);
console.log('file.size',file.size);
console.log('file.lastModified',file.lastModified);
console.log('file.lastModifiedDate',file.lastModifiedDate);
}
});
script>
body>
html>
不会实际读取文件的字节流
,来判断
它的媒体类型
,它基于文件扩展
来假设File 对象没有定义额外的方法,由于继承了 Blob 对象,也就继承了 slice方法,用法同上文 Blob 的 slice 方法。
slice([start[, end[, contentType]]])
slice 方法接收三个可选参数
:
start
和 end
都是数值
,表示截取的范围
contentType
指定截取的内容的 MIME 类型
。返回
一个新的 Blob对象
。FileReader
,URL.createObjectURL()
, createImageBitmap()
,和XMLHttpRequest.send()
都能处理 Blob 和 File。
var file1 = new File(['text1', 'text2'], 'test.txt', {type: 'text/plain'});
console.log(file1)
//△File{
// lastModified: 1631437933620
// lastModifiedDate: Sun Sep 12 2021 17:12:13 GMT+0800 (中国标准时间) {}
// name: "test.txt"
// size: 10
// type: "text/plain"
// webkitRelativePath: ""
//}
var file2 = file1.slice(3,6,'text/plain')
console.log(file2)
//△Blob {
// size: 3,
// type: "text/plain"
//}
FileReader 对象允许Web应用程序异步读取
存储在用户计算机上的文件
(或原始数据缓冲区
)的内容,使用 File
或 Blob 对象
指定要读取的文件或数据。
其中 File 对象,可以是来自用户在一个 元素
上选择文件后返回的 FileList
,也可以来自拖放
操作生成的 DataTransfer 对象
,还可以是来自在一个 HTMLCanvasElement 上执行 mozGetAsFile() 方法后返回结果。
var reader = new FileReader()
new FileReader()构造函数不需要传入参数
,返回一个 FileReader 的实例
。FileReader 继承 EventTarget对象。
FileReader实例属性
属性名称 | 读/写 | 描述 |
---|---|---|
error | 只读 | DOMException 的实例,表示在读取文件时发生的错误 。 |
result | 只读 | 表示文件的内容 ,该属性仅在读取操作完成后(load)后才有效 ,格式取决于读取方法 |
readyState | 只读 | 表示读取文件时状态的数字 |
备注:readeyState的取值如下:
0
------ EMPTY
--------- 还没有加载任何数据
1
------ LOADING
------ 数据正在被加载
2
------ DONE
----------- 已完成全部的读取请求
var reader = new FileReader();
console.log(reader.error); // null
console.log(reader.result); // null
console.log(reader.readyState); // 0
console.log(reader.EMPTY); // 0
console.log(reader.LOADING); // 1
console.log(reader.DONE); // 2
EMPTY、LOADING、DONE 这三个属性同时存在于 FileReader 和它的的原型对象上,因此实例上有这三个属性,FileReader 对象本身也有这三个属性:
console.log(FileReader.EMPTY); // 0
console.log(FileReader.LOADING); // 1
console.log(FileReader.DONE); // 2
文件的读取是一个异步的过程,和 XMLHttpRequest 对象一样,在读取操作过程中会触发一系列事件。
事件名称 | 描述 | 使用示例 |
---|---|---|
abort | 读取操作被中断时触发 | reader.onabort = function(event) {} |
error | 在读取操作发生错误时触发 | reader.onerror = function(event) {} |
load | 读取操作完成时触发 | reader.addEventListener(‘load’, function(event) {}) |
loadstart | 读取操作开始时触发 | reader.onloadstart = function(event) {} |
loadend | 读取操作结束时(要么成功,要么失败)触发 | reader.onloadend = function(event) {} |
progress | 在读取Blob时触发 | reader.onprogress = function(event) {} |
关于FileReader实例的方法,具有以下几种:
方法名称 | 描述 | 使用示例 |
---|---|---|
abort() | 中止读取操作。只有当 readyState 为 1 时才能调用 ;调用后,readyState 值为 2 在返回时,readyState属性为DONE。 |
reader.abort() |
readAsArrayBuffer(blob) | 读取指定的 Blob 或 File 对象中的内容。读取操作完成后,(触发loadend事件),result属性中保存的是被读取文件的 ArrayBuffer 数据对象 ,表示所读取的文件的数据内容 。 |
reader.readAsArrayBuffer(blob) |
readAsDataURL(blob) | 读取指定的 Blob 或 File 对象中的内容。读取操作完成后,(触发loadend事件),result属性中将保存一个 data:URL格式 的(base64编码字符串) ,以表示所读取文件的数据内容 。 |
reader.readAsArrayBuffer(file) |
readAsBinaryString(blob) | 已废弃,用 readAsArrayBuffer 代替 – | |
readAsText(blob[, encoding]) | 读取指定的 Blob 或 File 对象中的内容,将 Blob 或者 File 对象根据特殊的编码 格式转化为特定内容(字符串形式) ,默认编码是 utf-8 。读取完成后,result属性中将包含一个字符串 ,以表示所读取的文件内容 。 |
reader.readAsArrayBuffer(blob) |
【读取本地图片示例】:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<input type="file" id='file' accept="image/png, image/jpg, image/jpeg, image/gif" />
<img src="" alt="Image preview...">
</div>
<script>
var preview = document.querySelector('img');
var reader = new FileReader();
document.getElementById('file').addEventListener('change', function (event) {
console.log('--files--',this.files[0])
var file = this.files[0];
if (file) {
reader.readAsDataURL(file);
}
});
console.log('---reader--222---',reader)
reader.addEventListener("load", function () {
console.log('---reader.result---',reader.result)
preview.src = reader.result;
}, false);
</script>
</body>
</html>
data: URL格式的Base64字符串
(截图中没有展示完整)字符串
,形如: data:image/jpeg;base64,/9j/4QXERXhpZgAATU······
对于上面的示例,还可以使用URL.createObjectURL
的方法
objectURL = URL.createObjectURL(object)
其中:
object
:指用于创建 URL
的 File 对象
、Blob 对象
或者 MediaSource 对象
objectURL
:一个DOMString
,包含了一个对象URL
,该URL可用于指定源 object的内容
。<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<input type="file" id='file' accept="image/png, image/jpg, image/jpeg, image/gif" />
<br>
<img src="" alt="Image preview...">
</div>
<script>
var preview = document.querySelector('img');
var reader = new FileReader();
document.getElementById('file').addEventListener('change', function (event) {
console.log('--files--',this.files[0])
var file = this.files[0];
if (file) {
console.log('--url--',URL.createObjectURL(file))
preview.src = URL.createObjectURL(file);
}
});
</script>
</body>
</html>
ArrayBuffer 对象用来表示通用的
、固定长度
的原始二进制数据缓冲区
,是ES6 才纳入正式 ECMAScript 规范。
ArrayBuffer 类型化数组
,类型化数组是JavaScript操作二进制数据
的一个接口。最初为了满足JavaScript与显卡之间大量的、实时的数据交换,它们之间的数据通信必须是二进制的,而不能是传统的文本格式的背景下诞生的
ArrayBuffer 对象代表储存二进制数据
的一段内存
,它不能直接读写
,只能通过
视图(TypedArray视图
和DataView视图
)来读写,它们(视图)会将缓冲区中的数据用特定的格式进行解读,并通过这些格式来读写缓冲区的内容。
浏览器原生提供 ArrayBuffer() 构造函数
,用来生成实例
。
new ArrayBuffer(length)
length
:整数,表示二进制数据占用的字节长度
指定大小
的 ArrayBuffer 对象
,其内容被初始化为 0。let aa = new ArrayBuffer()
console.log(aa)
//下面为打印内容
ArrayBuffer(0)
byteLength: 0
__proto__: ArrayBuffer
[[Int8Array]]: Int8Array(0)
[[Uint8Array]]: Uint8Array(0)
[[Int16Array]]: Int16Array(0)
[[Int32Array]]: Int32Array(0)
[[ArrayBufferByteLength]]: 0
[[ArrayBufferData]]: "0x000000000000"
let aa = new ArrayBuffer(10)//实例对象 buffer 占用 10 个字节
console.log(aa)
//下面为打印内容
ArrayBuffer(10)
byteLength: (...)
__proto__: ArrayBuffer
[[Int8Array]]: Int8Array(10)
[[Uint8Array]]: Uint8Array(10)
[[Int16Array]]: Int16Array(5)
[[ArrayBufferByteLength]]: 10
[[ArrayBufferData]]: "0x002400a6a2b0"
ArrayBuffer 对象有实例属性 byteLength
,表示当前实例占用的内存字节长度
(单位字节
),一单创建就不可变更(只读`):
const buffer = new ArrayBuffer(32);
buffer.byteLength; // 32
ArrayBuffer.prototype.slice()
ArrayBuffer的实例
有一个slice
方法,允许将内存区域的一部分,拷贝生成一个新的ArrayBuffer对象。
slice(start , end)的参数:
开始复制的位置
。默认从 0 开始结束复制的位置
(不包括结束的位置)。如果省略,则表示复制到结束。const buff = new ArrayBuffer(32);
console.log(buff.byteLength);//32
const newBuffer = buff.slice(0, 3);
//以下为打印内容
ArrayBuffer(3)
byteLength: (...)
__proto__: ArrayBuffer
[[Int8Array]]: Int8Array(3)
[[Uint8Array]]: Uint8Array(3)
[[ArrayBufferByteLength]]: 3
[[ArrayBufferData]]: "0x002400a683b0"
除了slice方法,ArrayBuffer对象不提供任何直接读写内存的方法,只允许在其上方建立视图,然后通过视图读写
。
ArrayBuffer.isView()
ArrayBuffer本身
有一个静态方法isView
,返回一个布尔值
,表示参数是否为ArrayBuffer的视图实例
。这个方法大致相当于判断参数是否为TypedArray实例或DataView实例
。
const buffer = new ArrayBuffer(8);
ArrayBuffer.isView(buffer) // false
const v = new Int32Array(buffer);
ArrayBuffer.isView(v) // true
为了读写ArrayBuffer的实例,需要为它指定视图
ArrayBuffer对象作为内存区域,可以存放多种类型的数据。同一段内存,不同数据有不同的解读方式
,这就叫做“视图”
(view)
ArrayBuffer有两种
视图,
同一个数据类型
,TypedArray视图是用来向网卡、声卡之类的本机设备传送数据,所以使用本机的字节序就可以了不同的数据类型
,DataView视图是用来处理网络设备传来的数据,所以大端字节序或小端字节序是可以自行设定的字节序
DataView实例提供8个方法读取内存
。
getInt8:读取1个字节,返回一个8位整数。
getUint8:读取1个字节,返回一个无符号的8位整数。
getInt16:读取2个字节,返回一个16位整数。
getUint16:读取2个字节,返回一个无符号的16位整数。
getInt32:读取4个字节,返回一个32位整数。
getUint32:读取4个字节,返回一个无符号的32位整数。
getFloat32:读取4个字节,返回一个32位浮点数。
getFloat64:读取8个字节,返回一个64位浮点数。
var buf = new ArrayBuffer(32);
var dataView = new DataView(buf);
dataView.getUint8(0) // 0
同样,TypedArray对象一共提供9种类型的视图
,每一种视图都是一种构造函数。
Int8Array:8位有符号整数,长度1个字节。
Uint8Array:8位无符号整数,长度1个字节。
Uint8ClampedArray:8位无符号整数,长度1个字节,溢出处理不同。
Int16Array:16位有符号整数,长度2个字节。
Uint16Array:16位无符号整数,长度2个字节。
Int32Array:32位有符号整数,长度4个字节。
Uint32Array:32位无符号整数,长度4个字节。
Float32Array:32位浮点数,长度4个字节。
Float64Array:64位浮点数,长度8个字节。
var buffer = new ArrayBuffer(12);
var x1 = new Int32Array(buffer);
x1[0] = 1;
var x2 = new Uint8Array(buffer);
x2[0] = 2;
x1[0] // 2
上面代码对同一段内存,分别建立两种视图:32位带符号整数(Int32Array构造函数)和8位不带符号整数(Uint8Array构造函数)。由于两个视图对应的是同一段内存,一个视图修改底层内存,会影响到另一个视图
。
对于DataView视图和TypedArray视图更详细的讲解,可以参考博客:
ArrayBuffer 与 Blob 区别:
Blob
用于操作二进制文件
ArrayBuffer
用于操作内存
原始的二进制
数据缓冲区
,不能设置MIME类型
;Blob可以储存大量的二进制
编码格式的数据,可以设置对象的MIME类型
ArrayBuffer
的数据,是可以按照字节
去操作的,而Blob
的只能作为一个整的对象
去处理。ArrayBuffer相比Blob更接近真实的二进制,更底层
此处需要借助fileReader对象:
let blob = new Blob([1,2,3,4])
let reader = new FileReader();
reader.onload = function(result) {
console.log(result);
}
reader.readAsArrayBuffer(blob);
arraybuffer转blob很方便,作为参数传入就行了
var buffer = new ArrayBuffer(16)
var blob = new Blob([buffer])
本文部分参考博客: