为什么写
在我编写js代码中,关于处理二进制数据了解甚少,好像都是用数组表示,但是成员又很模糊。尤其是在遇到一些http的post请求或websocket,发送二进制数据(字节)时,还有一些算法的翻译,数据的转化,协议的复现,都需要不断的从网络上查阅,并未系统的从文档教程中入手。于是写这篇的目的就是为了加固对二进制数据的理解,以及JavaScript中如何操作二进制数据的。
ArrayBuffer
其他语言java,易所表示的是字节数组,字节集,而在js中则称二进制数组(都是用来表示二进制数据的),要注意的是这里的二进制数组并不是真正的数组,而是类似数组的对象。(后文会提到)
存储二进制数据用到的就是ArrayBuffer,但 ArrayBuffer不能直接读写,只能存储,需要通过视图来进行操作。
例如存储二进制数据的则是ArrayBuffer对象,例如请求图片时,就会指定参数 responseType: 'arraybuffer'表示返回二进制数据,也就是图片数据。
ArrayBuffer也是一个构造函数,可以分配一段可以存放数据的连续内存区域。
const buffer = new ArrayBuffer(8);ArrayBuffer {
[Uint8Contents]: <00 00 00 00 00 00 00 00>,
byteLength: 8
}这里的buffer.byteLength属性用于获取字节长度(返回32),直接打印buf的结果
其中还有一个slice方法,允许将内存区域的一部分,拷贝生成一个新的ArrayBuffer对象。下面代码拷贝buffer对象的前 3 个字节(从 0 开始,到第 3 个字节前面结束)
const buffer = new ArrayBuffer(8);
const newBuffer = buffer.slice(0, 3);除了slice方法,ArrayBuffer对象不提供任何直接读写内存的方法,只允许在其上方建立视图,然后通过视图读写。
TypedArray
不过只有空数据可没用,肯定需要操作ArrayBuffer,也就要介绍下TypedArray。
ArrayBuffer对象作为内存区域,可以存放多种类型的数据。同一段内存,不同数据有不同的解读方式,这就叫做“视图”(view),ArrayBuffer有两种视图,一种是TypedArray视图,另一种是DataView视图。这里只介绍TypedArray
TypedArray视图一共包括 9 种类型,每一种视图都是一种构造函数通过9个构造函数,可以生成9种数据格式的视图,比如Uint8Array(无符号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 |
视图的构造函数可以接受三个参数:
- 第一个参数(必需):视图对应的底层
ArrayBuffer对象。 - 第二个参数(可选):视图开始的字节序号,默认从 0 开始。
- 第三个参数(可选):视图包含的数据个数,默认直到本段内存区域结束。
演示
不妨给它写入字符串abc,对应的十进制ASCII码为97,98,99,由于ASCII码占用一个字节存储,所以这里选择Uint8Array用于表示
const buffer = new ArrayBuffer(8);
const buf = new Uint8Array(buffer);
buf.set([97, 98, 99]);
console.log(buf.buffer);
// 输出结果
ArrayBuffer {
[Uint8Contents]: <61 62 63 00 00 00 00 00>,
byteLength: 8
}可以看到abc确实存入了,并用十六进制的形式表示,为了验证,这里使用NodeJS中的Buffer来演示,当然也可以使用原生的TextEncoder
Buffer.from(buf.buffer).toString() // abc你也可以直接通过数组下标的形式,来访问数据,如buf[0]返回的就是97,但buf又有lengt与其他的属性方法,这种数组就统称为类数组。
buf还有一些方法,无非就是操作字节复制,偏移就不做过多介绍与演示了,具体文档可查看
NodeJS的Buffer
buffer 缓冲区 | Node.js API 文档 (nodejs.cn)
在Nodejs中有专门的操作ArrayBuffer 的对象Buffer,Buffer 类是 JavaScript Uint8Array 类的子类
所以Uint8Array有的属性方法Buffer也有,不过Nodejs对Buffer增加了额外的方法供开发者调用。
Buffer.from
上面的代码 Buffer.from(buf.buffer).toString(),也就是ArrayBuffer 数据转为utf8编码文本。其中toString还能转为以下编码(toString默认utf8)
type BufferEncoding = 'ascii' | 'utf8' | 'utf-8' | 'utf16le' | 'ucs2' | 'ucs-2' | 'base64' | 'base64url' | 'latin1' | 'binary' | 'hex';不过Nodejs不支持gbk编码,所以需要使用第三方包,如iconv-lite
Buffer.from()有多个方法实现,第一个参数可以传入ArrayBuffer | Uint8Array | string,如果是string类型,第二个参数为编码格式,例如实现编码转化
// base64
Buffer.from(str).toString('base64'); // 将str转base64编码
Buffer.from(str, 'base64').toString(); // 将base64编码转str
// hex
Buffer.from(str).toString('hex'); // 将str转hex编码
Buffer.from(str, 'hex').toString(); // 将hex编码转str封装Base64编码与解码
const Base64 = {
encode: (str) => {
return Buffer.from(str).toString('base64');
},
decode: (str) => {
return Buffer.from(str, 'base64').toString();
}
}buf.toJSON()
将会得到buf的视图类型,与二进制数组。
// let buf = Buffer.from('abc');
let buf = Buffer.from([97,98,99]);
console.log(buf); // <Buffer 61 62 63>
buf.toJSON() // { type: 'Buffer', data: [ 97, 98, 99 ] }
// 效果等同于 JSON.stringify(buf);
buf.values() // [ 97, 98, 99 ] 可以直接得到二进制数据官方文档: buffer 缓冲区 | Node.js API 文档 (nodejs.cn)
ArrayBuffer 和 Buffer 区别
上述对这两者进行了介绍,这里总结一下
ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区,是一个字节数组,可读但不可直接写。
Buffer 是 Node.JS 中用于操作 ArrayBuffer 的视图,继承自Uint8Array,是 TypedArray 的一种。
通俗点来说(对我而言),ArrayBuffer相当于其他语言的字节数组、字节集,但不可写,而Buffer 对象则是操作ArrayBuffer的。
应用
与二进制数据有关的地方就有应用
编码转化
将请求图片转化成base64编码
axios
.get('图片url地址', {
responseType: 'arraybuffer',
})
.then((res) => {
let base64Img = res.data.toString('base64');
console.log(base64Img);
});在axios请求图片数据的时候,指定responseType: 'arraybuffer',返回的data就是一个buffer对象。(当时写成这样的代码 Buffer.from(res.data).buffer,不过不妨碍)
http发送二进制数据与WebSocket
axios.post('http://example.com', Buffer.from('abc')).then((res) => {
console.log(res.data);
});let socket = new WebSocket('ws://127.0.0.1:8081');
socket.binaryType = 'arraybuffer';
// Wait until socket is open
socket.addEventListener('open', function (event) {
// Send binary data
const typedArray = new Uint8Array(4);
socket.send(typedArray.buffer);
});
// Receive binary data
socket.addEventListener('message', function (event) {
const arrayBuffer = event.data;
// ···
});文件读写
等等。。。
参考
















