buffer简介
nodejs引入bufferAPI使其可以在TCP流和文件系统操作等场景中处理二进制数据流。
buffer的大小在其创建时就已确定,且不能调整大小。
buffer类在nodejs中是一个全局变量,因此无需require(‘buffer’)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//创建一个长度为10、且用0填充的Buffer
const buf1 = Buffer.alloc(10);
//创建一个长度为10、且用0x1填充的Buffer。
const buf2 = Buffer.alloc(10,1);
//创建一个长度为10、且未初始化的Buffer。
//这个方法比调用Buffer.alloc()更快,
//但返回的Buffer实例可能包含旧数据,
//因此需要使用fill()或write()重写。
const buf3 = Buffer.allocUnsafe(10);
//创建一个包含[0x1,0x2,0x3]的Buffer。
const buf4 = Buffer.from([1,2,3]);
//创建一个包含ASCII字节数组[0x74,0x65,0x73,0x74]的Buffer。
const buf5 = Buffer.from('test');
//创建一个包含UTF-8字节数组[0x74,0xc3,0xa9,0x73,0x74]的Buffer
const buf6 = Buffer.from('test','utf8');
Buffer常用API
- Buffer.from(array)返回一个新建的包含所提供的字节数组的副本的Buffer
- Buffer.from(arrayBuffer[,byteOffset[,length]])返回一个新建的与给定的ArrayBuffer共享一内存的Buffer。
- Buffer.from(buffer)返回一个新建的包含所提供的buffer的内容的副本的buffer。
- Buffer.from(string[,encoding])返回一个新建的包含所提供的字符串的副本buffer.
- Buffer.alloc(size[,fill[,encoding]])返回一个指定的大小的被填满的Buffer实例,这个方法会明显地比Buffer.allocUnsafe(size) 慢,但可确保新创建的 Buffer 实例绝不会包含旧的和潜在的敏感数据。
- Buffer.allocUnsafe(size)与Buffer.allocUnsafeSlow(size)返回一个新建的指定 size 的 Buffer,但它的内容必须被初始化,可以使用 buf.fill(0) 或完全写满。
是什么令Buffer.allocUnsafe()和Buffer.alloUnsafeSlow()不安全
当调用 Buffer.allocUnsafe() 和 Buffer.allocUnsafeSlow() 时,被分配的内存段是未初始化的(没有用 0 填充)。 虽然这样的设计使得内存的分配非常快,但已分配的内存段可能包含潜在的敏感旧数据。 使用通过 Buffer.allocUnsafe() 创建的没有被完全重写内存的 Buffer ,在 Buffer 内存可读的情况下,可能泄露它的旧数据。
Buffer与字符编码
Buffer 实例一般用于表示编码字符的序列,比如 UTF-8 、 UCS2 、 Base64 、或十六进制编码的数据。 通过使用显式的字符编码,就可以在 Buffer 实例与普通的 JavaScript 字符串之间进行相互转换。1
2
3
4
5const buf = Buffer.from('hello world','ascii');
//输出68656c6c6f20776f726c64
console.log(buf.toString('hex'));
//输出aGVsbG8gd29ybGQ=
console.log(buf.toString('base64'));
nodejs目前支持的字符编码包括:
- ‘ascii’ - 仅支持 7 位 ASCII 数据。如果设置去掉高位的话,这种编码是非常快的。
- ‘utf8’ - 多字节编码的 Unicode 字符。许多网页和其他文档格式都使用 UTF-8 。
- ‘utf16le’ - 2 或 4 个字节,小字节序编码的 Unicode 字符。支持代理对(U+10000 至 U+10FFFF)。
- ‘base64’ - Base64 编码。当从字符串创建 Buffer 时,按照 RFC4648 第 5 章的规定,这种编码也将正确地接受“URL 与文件名安全字母表”。
- ‘latin1’ - 一种把 Buffer 编码成一字节编码的字符串的方式(由 IANA 定义在 RFC1345 第 63 页,用作 Latin-1 补充块与 C0/C1 控制码)。
- ‘hex’ - 将每个字节编码为两个十六进制字符。
Buffer与TypedArray
Buffer实例也是Unit8Array实例,但是与ECMAScript 2015 中的 TypedArray 规范还是有些微妙的不同。例如,当 ArrayBuffer#slice() 创建一个切片的副本时,Buffer#slice() 的实现是在现有的 Buffer 上不经过拷贝直接进行创建,这也使得 Buffer#slice() 更高效。
遵循以下注意事项,也可以从一个Buffer创建一个新的TypedArray实例:
- Buffer对象的内存是拷贝到typedarray的,而不是共享的。
- Buffer对象的内存是被解析为一个明确元素的数组,而不是一个目标类型的字节数组。也就是说,new Uint32Array(Buffer.from([1, 2, 3, 4])) 会创建一个包含 [1, 2, 3, 4] 四个元素的 Uint32Array,而不是一个只包含一个元素 [0x1020304] 或 [0x4030201] 的 Uint32Array 。
也可以通过 TypeArray 对象的 .buffer 属性创建一个新建的且与 TypedArray 实例共享同一分配内存的 Buffer 。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16const arr = new Unit16Array(2);
arr[0] = 5000;
arr[1] = 4000;
//拷贝'arr'的内容
const buf1 = Buffer.from(arr)
//与'arr'共享内存
const buf2 = Buffer.from(arr.buffer);
//输出:<Buffer 88 a0>
console.log(buf1);
//输出:<Buffer 88 13 a0 0f>
console.log(buf2);
arr[1] = 6000;
//输出:<Buffer 88 a0>
console.log(buf1);
//输出:<Buffer 88 13 70 17>
console.log(buf2);
当使用 TypedArray 的 .buffer 创建 Buffer 时,也可以通过传入 byteOffset 和 length 参数只使用 ArrayBuffer 的一部分。1
2
3
4const arr = new Uint16Array(20);
const buf = Buffer.from(arr.buffer,0,16);
//输出: 16
console.log(buf.length);
Buffer.from() 和 TypedArray.from() 有着不同的签名与实现。 具体而言,TypedArray 的变种接受第二个参数,在类型数组的每个元素上调用一次映射函数:
TypedArray.from(source[,mapFn[,thisArg]])
Buffer.from()方法不支持使用映射函数:
Buffer.from(array)
Buffer.from(buffer)
Buffer.from(arrayBuffer[,byteOfferset[,length]])
Buffer.from(string[,encoding])
Buffer与ES6迭代器
Buffer 实例可以使用 ECMAScript 2015 (ES6) 的 for..of 语法进行遍历。1
2
3
4
5
6
7
8const buf = Buffer.from([1,2,3]);
//输出:
// 1
// 2
// 3
for(var b of buf){
console.log(b);
}
此外,buf.values(),buf.keys()和buf.entries()方法可用于创建迭代器。
buffer应用
前面基本上说的是buffer的拼接和内存这两方面的。比如我们使用fs模块来读取文件内容的时候,返回的就是一个Buffer:1
2
3fs.readFile('filename',function(err,buf){
//<Buffer 2f 2a 2a 0a 20 2a 20 53 75 ...>
});
在使用net或http模块来接收网络数据时,data时间的参数也是一个Buffer,这时我们还需要使用Buffer.concat()来做数据拼接:1
2
3
4
5
6
7var bufs = [];
conn.on('data',function(buf){
bufs.push(buf);
});
conn.on('end',function(){
var buf = Buffer.concat(bufs);
});
还可以利用Buffer.toString()来做转换base64或十六进制字符的转换,比如:1
2
3
4
5
6
7
8console.log(new Buffer('hello,world!').toString('base64'));
//转换成base64字符串: aGVsbG8sIHdvcmxkIQ==
console.log(new Buffer('aGVsbG8sIHdvcmxkIQ==','base64').toString());
//还原base64字符串: hello,world!
console.log(new Buffer('hello,world!').toString('hex');
// 转换成十六进制字符串:68656c6c6f2c20776f726c6421
console.log(new Buffer('68656c6c6f2c20776f726c6421','hex').toString();
//还原十六进制字符串:hello,world!
这些API为在Node.js中操作数据提供了极大的便利。假设我们要将一个整形数值存储到文件中,比如当前时间戳为1447656645380,如果将其当作一个字符串存储时,需要占用11字节的空间,而将其转换为二进制存储时仅需6字节空间即可:1
2
3
4
5var buff = new Buffer(6);
buf.writeUIntBE(1447656645380, 0, 6);
// <Buffer 01 51 0f 0f 63 04>
buf.readUIntBE(0, 6);
// 1447656645380