二进制数组
概念介绍
ArrayBuffer
对象、TypedArray
视图和DataView
视图是 JavaScript 操作二进制数据的一个接口。它们都是以数组的语法处理二进制数据,所以统称为二进制数组。
二进制数组由三类对象组成。
(1)ArrayBuffer
对象:代表内存之中的一段二进制数据,可以通过“视图”进行操作。“视图”部署了数组接口,这意味着,可以用数组的方法操作内存。
(2)TypedArray
视图:共包括 9 种类型的视图,比如Uint8Array
(无符号 8 位整数)数组视图, Int16Array
(16 位整数)数组视图, Float32Array
(32 位浮点数)数组视图等等。
(3)DataView
视图:可以自定义复合格式的视图,比如第一个字节是 Uint8(无符号 8 位整数)、第二、三个字节是 Int16(16 位整数)、第四个字节开始是 Float32(32 位浮点数)等等,此外还可以自定义字节序。
-
简单说,
ArrayBuffer
对象代表原始的二进制数据,TypedArray
视图用来读写简单类型的二进制数据,DataView
视图用来读写复杂类型的二进制数据。
一、ArrayBuffer 对象
1. 概念介绍
ArrayBuffer
对象代表储存二进制数据的一段内存,它不能直接读写,只能通过视图(TypedArray
视图和- -DataView
视图)来读写,视图的作用是以指定格式解读二进制数据。通俗点的话就是arrbuffer是一个数组,只不过这个数组有点特殊,你只能看不能改,但是可以通过“视图”进行操作
2. 对象使用
ArrayBuffer
也是一个构造函数,可以分配一段可以存放数据的连续内存区域。
const buf = new ArrayBuffer(32);
上面代码生成了一段 32 字节的内存区域,每个字节的值默认都是 0。可以看到,ArrayBuffer
构造函数的参数是所需要的内存大小(单位字节)。
3. 实例属性和方法
ArrayBuffer
对象有实例属性 byteLength
,表示当前实例占用的内存字节长度(单位字节),一旦创建就不可变更(只读) :
const buffer = new ArrayBuffer(32); 其中32表示二进制数据占用的字节长度。
buffer.byteLength; // 32
ArrayBuffer
对象有实例方法 slice()
,用来复制一部分内存。
参数如下:
- start,整数类型,表示开始复制的位置。默认从 0 开始。
- end,整数类型,表示结束复制的位置(不包括结束的位置)。如果省略,则表示复制到结束。
const buffer = new ArrayBuffer(32);
const buffer2 = buffer.slice(0);
二、DataView 视图
1. 概念介绍
因为ArrayBuffer只能读,不能写,所以为了可以编辑数据,DataView 视图和TypedArray出现了,DataView 视图是一个可以从二进制ArrayBuffer对象中读写多种数值类型的底层接口,使用它时,不用考虑不同平台的字节序问题,主要用来读写复杂类型的二进制数据**。
2. 对象使用
const buf = new ArrayBuffer(32);
为了读写这段内容,需要为它指定视图。DataView
视图的创建,需要提供ArrayBuffer
对象实例作为参数。
const buf = new ArrayBuffer(32);
const dataView = new DataView(buf);
dataView.getUint8(0) // 0
上面代码对一段 32 字节的内存,建立DataView
视图,然后以不带符号的 8 位整数格式,从头读取 8 位二进制数据,结果得到 0,因为原始内存的ArrayBuffer
对象,默认所有位都是 0。
三、TypedArray 视图
1. 概念介绍
- 一个类型化数组(TypedArray)对象描述了一个底层的二进制数据缓冲区(binary data buffer)的一个类数组视图(view).
- 因为ArrayBuffer只能读不能写,没啥用。但是借助于TypedArray或者DataView就可以修改ArrayBuffer的内容
- 你可以把TypedArray想象成一个基类,其中Uint8Array、Float32Array等都继承于TypedArray(实际并不是继承!)
2. 对象使用
TypedArray
视图,与DataView
视图的一个区别是,它不是一个构造函数,而是一组构造函数,代表不同的数据格式。
const buffer = new ArrayBuffer(12);
const x1 = new Int32Array(buffer);
x1[0] = 1;
const x2 = new Uint8Array(buffer);
x2[0] = 2;
x1[0] // 2
上面代码对同一段内存,分别建立两种视图:32 位带符号整数(
Int32Array
构造函数)和 8 位不带符号整数(Uint8Array
构造函数)。由于两个视图对应的是同一段内存,一个视图修改底层内存,会影响到另一个视图。TypedArray
视图的构造函数,除了接受ArrayBuffer
实例作为参数,还可以接受普通数组作为参数,直接分配内存生成底层的ArrayBuffer
实例,并同时完成对这段内存的赋值。
const typedArray = new Uint8Array([0,1,2]);
typedArray.length // 3
typedArray[0] = 5;
typedArray // [5, 1, 2]
- 上面代码使用
TypedArray
视图的Uint8Array
构造函数,新建一个不带符号的 8 位整数视图。可以看到,Uint8Array
直接使用普通数组作为参数,对底层内存的赋值同时完成。
3. 不同类型的一组构造函数
- 解释其中一种,其他的类似:
Uint8Array
数组类型表示一个8位无符号整型数组,创建时内容被初始化为0,里面的每个元素只占一个字节。创建完后,可以以对象的方式或使用数组下标索引的方式引用数组中的元素。 -
TypedArray
视图支持的数据类型一共有 9 种(DataView
视图支持除Uint8C
以外的其他 8 种)。
数据类型 | 字节长度 | 含义 | 对应的 C 语言类型 |
---|---|---|---|
Int8 | 1 | 8 位带符号整数 | signed char |
Uint8 | 1 | 8 位不带符号整数 | unsigned char |
Uint8C | 1 | 8 位不带符号整数(自动过滤溢出) | unsigned char |
Int16 | 2 | 16 位带符号整数 | short |
Uint16 | 2 | 16 位不带符号整数 | unsigned short |
Int32 | 4 | 32 位带符号整数 | int |
Uint32 | 4 | 32 位不带符号的整数 | unsigned int |
Float32 | 4 | 32 位浮点数 | float |
Float64 | 8 | 64 位浮点数 | double |
4. 操作实例
-
实例主要实现了前端使用TypedArray编辑二进制
因为ArrayBuffer只读不可写,所以先把ArrayBuffer转换为可以编辑的TypedArray, 然后修改typedArray的内容, 接着再把二进制的数据转化为blob类型的数据,再把blob对象转化为一个url数据, 接着就可以把blob文件下载下来:
var ab = new ArrayBuffer(32)
var iA = new Int8Array(ab)
iA[0] = 97;//把二进制的数据的首位改为97 ,97为小写字母a的ascll码;
var blob = new Blob([iA], {type: "application/octet-binary"});//把二进制的码转化为blob类型
var url = URL.createObjectURL(blob);
window.open(url)
Blob
一、概念介绍
Blob
对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成ReadableStream
来用于数据操作。Blob 表示的不一定是JavaScript原生格式的数据。
File
接口基于Blob
,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。在实际Web应用中,Blob更多是图片二进制形式的上传与下载,虽然其可以实现几乎任意文件的二进制传输。
二、创建对象
- 创建Blob对象的方法有几种,可以调用Blob构造函数,还可以使用一个已有Blob对象上的
slice()
方法切出另一个Blob对象,还可以调用canvas
对象上的toBlob
方法。
三、属性
Blob对象有两个属性,参见下表:
属性名 | 类型 | 描述 |
---|---|---|
size | unsigned long long(表示可以很大的数值) | Blob对象中所包含数据的大小。字节为单位。 只读。 |
type | DOMString | 一个字符串,表明该Blob对象所包含数据的MIME类型。例如,上demo图片MIME类似就是”image/jpeg “. 如果类型未知,则该值为空字符串。 只读。
|
四、构造函数
构造函数
与FormData对象类似,Blob也有一个构造函数用法。语法如下:
Blob Blob(
[可选] Array parts,
[可选] BlobPropertyBag properties
);
例如:
var myBlob= new Blob(arrayBuffer);
其中,两个参数的含义是:
-
parts
一个数组,包含了将要添加到Blob对象中的数据。数组元素可以是任意多个的ArrayBuffer, ArrayBufferView(typed array), Blob, 或者DOMString对象。
-
properties
一个对象,设置Blob对象的一些属性。目前仅支持一个
type
属性,表示Blob的类型。
五、方法
Blob对象有个很重要的方法-slice()
,作用是,可以实现文件的分割!
具体使用方法如下,和数组slice相似:
Blob slice(
[可选] long long start,
[可选] long long end,
[可选] DOMString contentType
};
参数释义:
-
start
开始索引,可以为负数,语法类似于数组的
slice
方法。默认值为0. -
end
结束索引,可以为负数,语法类似于数组的
slice
方法。默认值为最后一个索引。 -
contentType
- 新的Blob对象的MIME类型,这个值将会成为新的Blob对象的type属性的值,默认为一个空字符串。
- 显然,此方法返回的数据格式还是Blob对象,不过是指定范围复制的新的Blob对象。注意,如果
start
参数的值比源Blob对象的size
属性值还大,则返回的Blob对象的size
值为0
,也就是不包含任何数据。
- 显然,此方法返回的数据格式还是Blob对象,不过是指定范围复制的新的Blob对象。注意,如果
四、举例
Blob获取图片并二进制显示demo
var eleAppend = document.getElementById("forAppend");
window.URL = window.URL || window.webkitURL;
if (typeof history.pushState == "function") {
var xhr = new XMLHttpRequest();
xhr.open("get", "/image/study/s/s256/mm1.jpg", true);
xhr.responseType = "blob";
xhr.onload = function() {
if (this.status == 200) {
var blob = this.response;
var img = document.createElement("img");
img.onload = function(e) {
window.URL.revokeObjectURL(img.src); // 清除释放
};
img.src = window.URL.createObjectURL(blob);
eleAppend.appendChild(img);
}
}
xhr.send();
} else {
eleAppend.innerHTML = '<p style="color:#cd0000;">浏览器不给力,还是早点回去给孩子喂奶吧~</p>';
}
Arraybuffer和Blob的区别
ArrayBuffer
其实就是一块连续内存,所以是low-level的。你可以将这块内存映射为某种数组(TypedArray
)或者是自定义的数据视图(DataView
),将来如果JS有了Struct(或TypedObject
),有可能可以映射为结构体(Struct)或结构体数组。Blob
则是一个相对high-level的概念,来自于数据库,可以认为就是「文件」(所以blob是有文件类型的,即mime type),只不过是脱离具体文件系统的文件(不需要有文件名、文件路径之类的东西)。Blob
对象并不对应内存,一个blob
引用更像文件句柄,你读取blob的内容,可以是全放进一个ArrayBuffer里,也可以直接得到一个字符串(如果是文本文件),还可以通过Stream来读取,特别是blob很大的情况下内存也放不下,只能通过流处理。注意,
Blob
并不像ArrayBuffer
是JS语言内置的,而是Web API,Node.js的API里就没有Blob。这也是为什么MDN说Blob表示的不一定是JavaScript原生格式的数据。Blob
用于操作二进制文件ArrayBuffer
用于操作内存大白话来说就是ArrayBuffer表达的是一片可编辑的内存,很类似于Node里的Buffer或者其它语言里动态分配的内存。里面的数据是可读可写的。而Blob表示的是一个做为一个整体的二进制文件,更多的目的是直接使用它
ArrayBuffer和TypedArray,以及Blob的使用
1. 前端使用TypedArray编辑二进制
var ab = new ArrayBuffer(32)
var iA = new Int8Array(ab)
iA[0] = 97;//把二进制的数据的首位改为97 ,97为小写字母a的ascll码;
var blob = new Blob([iA], {type: "application/octet-binary"});//把二进制的码转化为blob类型
var url = URL.createObjectURL(blob);
window.open(url)
2. FileReader读区blob文件
var ab = new ArrayBuffer(32)
var iA = new Int8Array(ab)
iA[0] = 97
var blob = new Blob([iA], {type: "application/octet-binary"});
var fr = new FileReader();
fr.addEventListener("load", function(ev) {
console.log(ev.target.result);//会输出字符:a
});
fr.readAsText(blob)
3. blob转化为typedArray
var ab = new ArrayBuffer(32)
var iA = new Int8Array(ab)
iA[0] = 97
var blob = new Blob([iA], {type: "application/octet-binary"});
var fr = new FileReader();
fr.addEventListener("load", function(ev) {
var abb = ev.target.result;
var iAA = new Int8Array(abb);
console.log(iAA);
});
//把blob文件转化为arraybuffer;
fr.readAsArrayBuffer(blob)
arraybuffer -->> typedarray -->> blob -->> blob通过FileReader转化为 arraybuffer或者text文本或者base64字符串;
arraybuffer和typedarray主要是处理二进制, 如果要把blob往二进制转换, 必须先把blob转换为arraybuffer, 然后再转换为可以编辑的typedArray;
实际上, 还有一种比较常用的数据类型, base64编码的数据, 常用的比如image的base64的编码, 文本的base64编码等, 也可以把base64的编码转化为对应的ascll码,再转化为typearray ,然后再生成blob对象:
4. base64的编码生成blob
function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
//通过atob把base64转化为ascll码, 然后再把ascll码转化为字节码
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
//u8arr就是2进制的数据
return new Blob([u8arr], {type:mime});
}