NodeJS文档之Buffer(1)-Buffer的简介

Buffer

在ECMAScript 2015(ES6)中引入TypedArray以前,JavaScript中没有读取或操做二进制数据流的机制。 Buffer类做为Node.js标准API的一部分被引入,使得能够在诸如TCP流和文件系统操做的上下文中经过8位的字节流进行交互。javascript

如今TypedArray已经添加到ES6中,Buffer类以一种更优化和适用于Node.js的方式实现了Uint8Array的API。java

Buffer类的实例似于整数数组,不过是对应V8堆外的固定大小的原始内存进行分配(堆外内存,不受V8引擎控制)。 缓冲区的大小在建立时已经肯定,不能调整大小。node

Buffer类是Node.js中的一个全局变量,所以不须要使用require('buffer').Buffer数组

// 建立一个长度为10的『0填充』Buffer实例
const buf1 = Buffer.alloc(10);

// 建立一个长度为10的Buffer,填充0x1
const buf2 = Buffer.alloc(10, 0x1);

// 建立一个长度为10的未初始化缓冲区
// 这个方法比使用Buffer.alloc()快,可是返回的Buffer实例可能包含以前的老数据。
// 须要使用fill()或write()重写。
const buf3 = Buffer.allocUnsafe(10);

// 建立一个包含[0x1, 0x2, 0x3]的缓冲区
const buf4 = Buffer.from([1, 2, 3]);

// 建立一个包含UTF-8字节[0x74, 0xc3, 0xa9, 0x73, 0x74]的缓冲区
const buf5 = Buffer.from('tést');

// 建立一个包含Latin-1字节[0x74, 0xe9, 0x73, 0x74]的缓冲区
const buf6 = Buffer.from('tést', 'latin-1');

Buffer.from(),Buffer.alloc(),Buffer.allocUnsafe()

在v6以前的Node.js版本中,使用Buffer构造函数建立了Buffer实例,该函数根据提供的参数以不一样的方式分配返回的Buffer:浏览器

  • 将一个数字做为第一个参数传递给Buffer() (例如new Buffer(10)),分配一个指定大小的新的Buffer对象。 为这些缓冲区实例分配的内存不会被初始化,而且可能包含敏感数据。 这种缓冲区实例必须经过使用buf.fill(0)或彻底写入缓冲区来手动初始化。 虽然这种行为是为了提升性能,可是开发经验代表,建立一个快速但未初始化的缓冲区和建立一个更慢但更安全的缓冲区之间须要更明确的区分。安全

  • 传递字符串,数组或Buffer做为第一个参数将传递的对象的数据复制到缓冲区中。bash

  • 传递ArrayBuffer返回一个与给定的ArrayBuffer共享已分配内存的Buffer。服务器

由于new Buffer()的行为是基于第一个参数传递的值的类型来选择具体操做,若是没有传递正确的参数给给new Buffer()或未能适当地初始化新分配的Buffer中缓冲区内容。那么此类应用将在无心中将安全性和可靠性的相关问题引入到他们的代码中。函数

为了使缓冲区实例的建立更可靠,出现更少的错误,new Buffer()造函数的这种形式已被弃用,使用Buffer.from()Buffer.alloc()Buffer.allocUnsafe()方法来代替。性能

开发人员应该将如今使用的new Buffer()函数迁移到如下这些新的API之一。

  • Buffer.from(array)返回一个包含所提供的八位字节的副本的新缓冲区。

  • Buffer.from(arrayBuffer [,byteOffset [,length]])返回一个新的缓冲区,它与给定的ArrayBuffer共享相同的分配内存。

  • Buffer.from(buffer)返回一个包含给定Buffer的内容的副本的新缓冲区。

  • Buffer.from(string [,encoding])返回一个包含所提供字符串副本的新缓冲区。

  • Buffer.alloc(size [,fill [,encoding]])返回指定大小的“已填充”Buffer实例。 此方法虽然明显慢于Buffer.allocUnsafe(size)方法,但它能够确保新建立的Buffer实例不会包含旧的和潜在的敏感数据。

  • Buffer.allocUnsafe(size)Buffer.allocUnsafeSlow(size)都返回一个指定大小的新的缓冲区,其内容必须使用buf.fill(0)初始化或彻底写入新的内容以覆盖旧的敏感数据。

Buffer.allocUnsafe()返回的缓冲区实例的大小小于或等于Buffer.poolSize的一半那么能够在内部共享内存池中进行分配。 而Buffer.allocUnsafeSlow()返回的实例不会使用内部共享内存池。


--zero-fill-buffers命令行参数

Node.js可使用--zero-fill-buffers命令行参数启动,强制全部新分配的Buffer实例在使用new Buffer(size)Buffer.allocUnsafe()Buffer.allocUnsafeSlow()或者new SlowBuffer()在建立时自动0。 使用此参数会更改这些方法的默认行为,而且会对性能产生重大影响。 建议仅在必要时强制使用--zero-fill-buffers选项,以确保新分配的缓冲区实例不会包含潜在敏感数据。

$ node --zero-fill-buffers
> Buffer.allocUnsafe(5);
<Buffer 00 00 00 00 00>

为何Buffer.allocUnsafe()Buffer.allocUnsafeSlow()不安全?

当调用Buffer.allocUnsafe()Buffer.allocUnsafeSlow()时,分配的内存段未初始化(不归0)。 虽然该设计使得存储器的分配至关快,可是所分配的存储器段可能包含潜在敏感的旧数据。 使用由Buffer.allocUnsafe()建立的缓冲区而不彻底从新覆盖的内存可能在读取缓冲区内存时泄漏这些旧数据。

虽然使用Buffer.allocUnsafe()有明显的性能优点,但必须格外当心,以免将安全漏洞引入应用程序。


Buffers和字符编码

Buffer实例一般用于表示编码字符的序列,例如UTF-8,UCS2,Base64甚至Hex编码数据。 能够经过使用字符编码声明字符串在缓冲区实例和普通JavaScript字符串之间来回转换。

const buf = Buffer.from('hello world', 'ascii');

console.log(buf.toString('hex')); // => 68656c6c6f20776f726c64

console.log(buf.toString('base64')); // => aGVsbG8gd29ybGQ=

Node.js当前支持的字符编码包括:

  • ascii

  • utf8

  • utf16le

  • ucs2

  • base64

  • latin1

  • binary

  • hex

注意:当今的浏览器遵循WHATWG规范,将“latin1”和ISO-8859-1都合并到win-1252。 这意味着,当作相似于http.get()的操做时,若是返回的字符集是在WHATWG规范列出的范围内,服务器可能实际返回的是win-1252的编码数据,此时使用“latin1”编码可能会错误地解码字符,从而出现乱码。


Buffers和TypeArray

Buffer实例也是Uint8Array实例。 可是,与ECMAScript 2015中的TypedArray规范有一点点的不兼容。例如,当ArrayBuffer#slice()建了一个slice的副本时,Buffer#slice()实现建立了一个没有复制的现有Buffer的副本,使得Buffer#slice()更有效率。

Buffer中建立新的TypedArray实例时须要注意如下事项:

  1. Buffer对象的内存是从TypedArray复制过来的,不是共享。

  2. Buffer对象的内存能够解释为不一样元素的数组,而不是目标类型的字节数组。 也就是说,new Uint32Array(Buffer.from([1,2,3,4]))是经过数组[1,2,3,4]建立一个长度为4的Uint32Array,而不是经过[0x1020304][0x4030201]建立一个长度为1的Uint32Array

能够经过使用TypeArray对象的.buffer属性建立一个新的Buffer,它与TypedArray实例共享相同的分配内存。

const arr = new Uint16Array(2);

arr[0] = 5000;
arr[1] = 4000;

const buf1 = Buffer.from(arr);

const buf2 = Buffer.from(arr.buffer);

console.log(buf1); // => <Buffer 88 a0>

console.log(buf2); // => <Buffer 88 13 a0 0f>

arr[1] = 6000;

console.log(buf1); // => <Buffer 88 a0>

console.log(buf2); // => <Buffer 88 13 70 17>

注意,当使用TypedArray的.buffer建立一个Buffer时,能够经过传递byteOffsetlength参数来使用ArrayBuffer的一部份内容。

const arr = new Uint16Array(20);
const buf = new Buffer.from(arr.buffer, 0, 16);

console.log(buf.length); // => 16

Buffer.from()TypedArray.from()有不一样的函数签名和实现。 具体来讲,TypedArray变量接受第二个参数,它是在类型数组的每一个元素上调用的映射函数:

  • TypedArray.from(source[, mapFn[, thisArg]])

Float32Array.from([1, 2, 3], x => x + x);      
// Float32Array [ 2, 4, 6 ]

然而,Buffer.from()方法不支持使用映射函数:

  • Buffer.from(array)

  • Buffer.from(buffer)

  • Buffer.from(arrayBuffer[, byteOffset [, length]])

  • Buffer.from(string[, encoding])


Buffers和ES6的迭代器

可使用ECMAScript 2015(ES6)for..of语法对缓冲区实例进行迭代。

const buf = Buffer.from([1, 2, 3]);

for (const b of buf) {
    console.log(b)); // => 1 2 3
}

此外,buf.values()buf.keys()buf.entries()方法均可用于建立迭代器。

相关文章
相关标签/搜索