开发手册 欢迎您!
软件开发者资料库

缓存(Buffer) | Node.js

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。Node.js 的包管理器 npm,是全球最大的开源库生态系统。

Node.js v8.x 中文文档


目录

Buffer (缓冲)#

稳定性: 2 - 稳定的

ECMAScript 2015 (ES6) 引入 TypedArray 之前,JavaScript 语言没有读取或操作二进制数据流的机制。Buffer 类被引入作为 Node.js API 的一部分,使其可以在 TCP 流或文件系统操作等场景中处理二进制数据流。

TypedArray 现已被添加进 ES6 中,Buffer 类以一种更优化、更适合 Node.js 用例的方式实现了 Uint8Array API。

Buffer 类的实例类似于整数数组,但 Buffer 的大小是固定的、且在 V8 堆外分配物理内存。Buffer 的大小在被创建时确定,且无法调整。

Buffer 类在 Node.js 中是一个全局变量,因此无需使用 require('buffer').Buffer

例子:

// 创建一个长度为 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]);// 创建一个包含 UTF-8 字节 [0x74, 0xc3, 0xa9, 0x73, 0x74] 的 Buffer。const buf5 = Buffer.from('tést');// 创建一个包含 Latin-1 字节 [0x74, 0xe9, 0x73, 0x74] 的 Buffer。const buf6 = Buffer.from('tést', 'latin1');

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

在 Node.js v6 之前的版本中,Buffer 实例是通过 Buffer 构造函数创建的,它根据提供的参数返回不同的 Buffer

  • 传一个数值作为第一个参数给 Buffer()(如 new Buffer(10)),则分配一个指定大小的新建的 Buffer 对象。在 Node.js 8.0.0 之前,分配给这种 Buffer 实例的内存是没有初始化的,且可能包含敏感数据。这种 Buffer 实例随后必须被初始化,可以使用 buf.fill(0) 或写满这个 Buffer。虽然这种行为是为了提高性能而有意为之的,但开发经验表明,创建一个快速但未初始化的 Buffer 与创建一个慢点但更安全的 Buffer 之间需要有更明确的区分。从 Node.js 8.0.0 开始, Buffer(num)new Buffer(num) 将返回一个初始化内存之后的 Buffer
  • 传一个字符串、数组、或 Buffer 作为第一个参数,则将所传对象的数据拷贝到 Buffer 中。
  • 传入 ArrayBufferSharedArrayBuffer,则返回一个与传入的 ArrayBuffer 共享所分配内存的 Buffer

因为 new Buffer() 的行为会根据所传入的第一个参数的值的数据类型而明显地改变,所以如果应用程序没有正确地校验传给 new Buffer() 的参数、或未能正确地初始化新分配的 Buffer 的内容,就有可能在无意中为他们的代码引入安全性与可靠性问题。

为了使 Buffer 实例的创建更可靠、更不容易出错,各种 new Buffer() 构造函数已被 废弃,并由 Buffer.from()Buffer.alloc()、和 Buffer.allocUnsafe() 方法替代。

开发者们应当把所有正在使用的 new Buffer() 构造函数迁移到这些新的 API 上。

如果 size 小于或等于 Buffer.poolSize 的一半,则 Buffer.allocUnsafe() 返回的 Buffer 实例可能会被分配进一个共享的内部内存池。

--zero-fill-buffers 命令行选项#

Node.js 可以在一开始就使用 --zero-fill-buffers 命令行选项强制所有使用 new Buffer(size)Buffer.allocUnsafe()Buffer.allocUnsafeSlow()new SlowBuffer(size) 新分配的 Buffer 实例在创建时自动用 0 填充。使用这个选项会改变这些方法的默认行为,且对性能有明显的影响。建议只在需要强制新分配的 Buffer 实例不能包含潜在的敏感数据时才使用 --zero-fill-buffers 选项。

例子:

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

是什么令 Buffer.allocUnsafe()Buffer.allocUnsafeSlow() 不安全?#

当调用 Buffer.allocUnsafe()Buffer.allocUnsafeSlow() 时,被分配的内存段是未初始化的(没有用 0 填充)。虽然这样的设计使得内存的分配非常快,但已分配的内存段可能包含潜在的敏感旧数据。使用通过 Buffer.allocUnsafe() 创建的没有被完全重写内存的 Buffer ,在 Buffer 内存可读的情况下,可能泄露它的旧数据。

虽然使用 Buffer.allocUnsafe() 有明显的性能优势,但必须额外小心,以避免给应用程序引入安全漏洞。

Buffer 与字符编码#

Buffer 实例一般用于表示编码字符的序列,比如 UTF-8 、 UCS2 、 Base64 、或十六进制编码的数据。通过使用显式的字符编码,就可以在 Buffer 实例与普通的 JavaScript 字符串之间进行相互转换。

例子:

const buf = Buffer.from('hello world', 'ascii');// 输出 68656c6c6f20776f726c64console.log(buf.toString('hex'));// 输出 aGVsbG8gd29ybGQ=console.log(buf.toString('base64'));

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

  • 'ascii' - 仅支持 7 位 ASCII 数据。如果设置去掉高位的话,这种编码是非常快的。

  • 'utf8' - 多字节编码的 Unicode 字符。许多网页和其他文档格式都使用 UTF-8 。

  • 'utf16le' - 2 或 4 个字节,小字节序编码的 Unicode 字符。支持代理对(U+10000 至 U+10FFFF)。

  • 'ucs2' - 'utf16le' 的别名。

  • 'base64' - Base64 编码。当从字符串创建 Buffer 时,按照 RFC4648 第 5 章的规定,这种编码也将正确地接受“URL 与文件名安全字母表”。

  • 'latin1' - 一种把 Buffer 编码成一字节编码的字符串的方式(由 IANA 定义在 RFC1345 第 63 页,用作 Latin-1 补充块与 C0/C1 控制码)。

  • 'binary' - 'latin1' 的别名。

  • 'hex' - 将每个字节编码为两个十六进制字符。

注意:现代浏览器遵循 WHATWG 编码标准 将 'latin1' 和 ISO-8859-1 别名为 win-1252。这意味着当进行例如 http.get() 这样的操作时,如果返回的字符编码是 WHATWG 规范列表中的,则有可能服务器真的返回 win-1252 编码的数据,此时使用 'latin1' 字符编码可能会错误地解码数据。

Buffer 与 TypedArray#

Buffer 实例也是 Uint8Array 实例。但是与 ECMAScript 2015 中的 TypedArray 规范还是有些微妙的不同。例如,当 ArrayBuffer#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] 四个元素的 Uint32Array,而不是一个只包含一个元素 [0x1020304][0x4030201]Uint32Array

也可以通过 TypeArray 对象的 .buffer 属性创建一个新建的且与 TypedArray 实例共享同一分配内存的 Buffer

例子:

const arr = new Uint16Array(2);arr[0] = 5000;arr[1] = 4000;// 拷贝 `arr` 的内容const buf1 = Buffer.from(arr);// 与 `arr` 共享内存const buf2 = Buffer.from(arr.buffer);// 输出: console.log(buf1);// 输出: console.log(buf2);arr[1] = 6000;// 输出: console.log(buf1);// 输出: console.log(buf2);

注意,当使用 TypedArray.buffer 创建 Buffer 时,也可以通过传入 byteOffsetlength 参数只使用 ArrayBuffer 的一部分。

例子:

const arr = new Uint16Array(20);const buf = Buffer.from(arr.buffer, 0, 16);// 输出: 16console.log(buf.length);

Buffer.from()TypedArray.from() 有着不同的签名与实现。具体而言,TypedArray 的变种接受第二个参数,在类型数组的每个元素上调用一次映射函数:

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

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

Buffer 与 ES6 迭代器#

Buffer 实例可以使用 ECMAScript 2015 (ES6) 的 for..of 语法进行遍历。

例子:

const buf = Buffer.from([1, 2, 3]);// 输出://   1//   2//   3for (const b of buf) {  console.log(b);}

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

Buffer 类#

Buffer 类是一个全局变量类型,用来直接处理二进制数据的。它能够使用多种方式构建。

new Buffer(array)#

稳定性: 0 - 废弃的: 使用 Buffer.from(array) 代替。
  • array 要从中复制的字节数组。

使用八字节的 array 分配一个新的 Buffer

例子:

// 创建一个新的包含字符串 'buffer' 的 UTF-8 编码的 Bufferconst buf = new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]);

new Buffer(arrayBuffer[, byteOffset [, length]])#

稳定性: 0 - 废弃的: 使用 [Buffer.from(arrayBuffer[, byteOffset [, length]])]Buffer.from(arrayBuffer) 代替。
  • arrayBuffer | ArrayBufferSharedArrayBufferTypedArray.buffer 属性。
  • byteOffset 开始拷贝的索引。 默认: 0
  • length 拷贝的字节数。默认: arrayBuffer.length - byteOffset

该方法将创建 ArrayBufferSharedArrayBuffer 的视图,而不会复制底层内存。例如,当传入一个 TypedArray 实例的 .buffer 属性的引用时,这个新建的 Buffer 会像 TypedArray 那样共享同一段分配的内存。

可选的 byteOffsetlength 参数指定将与 Buffer 共享的 arrayBuffer 的内存范围。

例子:

const arr = new Uint16Array(2);arr[0] = 5000;arr[1] = 4000;// 与 `arr` 共享内存const buf = new Buffer(arr.buffer);// 输出: console.log(buf);// 改变原始的 Uint16Array 也将改变 Bufferarr[1] = 6000;// 输出: console.log(buf);

new Buffer(buffer)#

稳定性: 0 - 废弃的: 使用 Buffer.from(buffer) 代替。
  • buffer 一个要拷贝数据的已存在的 Buffer

将传入的 buffer 数据拷贝到一个新建的 Buffer 实例。

例子:

const buf1 = new Buffer('buffer');const buf2 = new Buffer(buf1);buf1[0] = 0x61;// 输出: aufferconsole.log(buf1.toString());// Prints: bufferconsole.log(buf2.toString());

new Buffer(size)#

稳定性: 0 - 废弃的: 使用 Buffer.alloc() 代替(或 Buffer.allocUnsafe())。
  • size 新建的 Buffer 期望的长度

分配一个大小为 size 字节的新建的 Buffer。如果 size 大于 buffer.constants.MAX_LENGTH 或小于 0,则抛出 RangeError 错误。如果 size 为 0,则创建一个长度为 0 的 Buffer

在 Node.js 8.0.0 之前,以这种方式创建的 Buffer 实例的底层内存是 未初始化 的。新建的 Buffer 的内容是未知的并且 可能包含敏感数据。使用 Buffer.alloc(size) 代替它去初始化 Buffer 为 0。

例子:

const buf = new Buffer(10);// 输出: console.log(buf);

new Buffer(string[, encoding])#

稳定性: 0 - 废弃的: 使用 Buffer.from(string[, encoding]) 代替。
  • string 要编码的字符串
  • encoding string 的字符串编码。 默认: 'utf8'

创建一个包含给定字符串 stringBufferencoding 参数制定 string 的字符串编码。

例子:

const buf1 = new Buffer('this is a tést');// 输出: this is a téstconsole.log(buf1.toString());// 输出: this is a tC)stconsole.log(buf1.toString('ascii'));const buf2 = new Buffer('7468697320697320612074c3a97374', 'hex');// 输出: this is a téstconsole.log(buf2.toString());

类方法:Buffer.alloc(size[, fill[, encoding]])#

  • size 新建的 Buffer 期望的长度
  • fill | | 用来预填充新建的 Buffer 的值。默认: 0
  • encoding 如果 fill 是字符串,则该值是它的字符编码。默认: 'utf8'

分配一个大小为 size 字节的新建的 Buffer 。如果 fillundefined ,则该 Buffer 会用 0 填充

例子:

const buf = Buffer.alloc(5);// 输出: console.log(buf);

分配一个大小为 size 字节的新建的 Buffer 。如果 size 大于 buffer.constants.MAX_LENGTH 或小于 0,则抛出 RangeError 错误。如果 size 为 0,则创建一个长度为 0 的 Buffer

如果指定了 fill ,则会调用 buf.fill(fill) 初始化分配的 Buffer

例子:

const buf = Buffer.alloc(5, 'a');// 输出: console.log(buf);

如果同时指定了 fillencoding ,则会调用 buf.fill(fill, encoding) 初始化分配的 Buffer

例子:

const buf = Buffer.alloc(11, 'aGVsbG8gd29ybGQ=', 'base64');// 输出: console.log(buf);

调用 Buffer.alloc() 会明显地比另一个方法 Buffer.allocUnsafe() 慢,但是能确保新建的 Buffer 实例的内容不会包含敏感数据

如果 size 不是一个数值,则抛出 TypeError 错误。

类方法:Buffer.allocUnsafe(size)#

  • size 新建的 Buffer 期望的长度

分配一个大小为 size 字节的新建的 Buffer 。如果 size 大于 buffer.constants.MAX_LENGTH 或小于 0,则抛出 RangeError 错误。如果 size 为 0,则创建一个长度为 0 的 Buffer

以这种方式创建的 Buffer 实例的底层内存是未初始化的。新创建的 Buffer 的内容是未知的,且可能包含敏感数据。可以使用 buf.fill(0) 初始化 Buffer 实例为0。

例子:

const buf = Buffer.allocUnsafe(10);// 输出: (内容可能不同): console.log(buf);buf.fill(0);// 输出: console.log(buf);

如果 size 不是一个数值,则抛出 TypeError 错误。

注意,Buffer 模块会预分配一个大小为 Buffer.poolSize 的内部 Buffer 实例作为快速分配池,用于使用 Buffer.allocUnsafe() 新创建的 Buffer 实例,以及废弃的 new Buffer(size) 构造器,仅限于当 size 小于或等于 Buffer.poolSize >> 1Buffer.poolSize 除以2后的最大整数值)。

对这个预分配的内部内存池的使用,是调用 Buffer.alloc(size, fill)Buffer.allocUnsafe(size).fill(fill) 的关键区别。具体地说,Buffer.alloc(size, fill) 永远不会使用这个内部的 Buffer 池,但如果 size 小于或等于 Buffer.poolSize 的一半, Buffer.allocUnsafe(size).fill(fill) 使用这个内部的 Buffer 池。当应用程序需要 Buffer.allocUnsafe() 提供额外的性能时,这个细微的区别是非常重要的。

类方法:Buffer.allocUnsafeSlow(size)#

  • size 新建的 Buffer 期望的长度

分配一个大小为 size 字节的新建的 Buffer 。如果 size 大于 buffer.constants.MAX_LENGTH 或小于 0,则抛出 RangeError 错误。如果 size 为 0,则创建一个长度为 0 的 Buffer

以这种方式创建的 Buffer 实例的底层内存是未初始化的。新创建的 Buffer 的内容是未知的,且可能包含敏感数据。可以使用 buf.fill(0) 初始化 Buffer 实例为0。

当使用 Buffer.allocUnsafe() 分配新建的 Buffer 时,当分配的内存小于 4KB 时,默认会从一个单一的预分配的 Buffer 切割出来。这使得应用程序可以避免垃圾回收机制因创建太多独立分配的 Buffer 实例而过度使用。这个方法通过像大多数持久对象一样消除追踪与清理的需求,改善了性能与内存使用。

当然,在开发者可能需要在不确定的时间段从内存池保留一小块内存的情况下,使用 Buffer.allocUnsafeSlow() 创建一个非池的 Buffer 实例然后拷贝出相关的位元是合适的做法。

例子:

// 需要保留一小块内存块const store = [];socket.on('readable', () => {  const data = socket.read();  // 为保留的数据分配内存  const sb = Buffer.allocUnsafeSlow(10);  // 拷贝数据进新分配的内存  data.copy(sb, 0, 0, 10);  store.push(sb);});

Buffer.allocUnsafeSlow() 应当仅仅作为开发者已经在他们的应用程序中观察到过度的内存保留之后的终极手段使用。

如果 size 不是一个数值,则抛出 TypeError 错误。

类方法:Buffer.byteLength(string[, encoding])#

  • string | | | | | 要计算长度的值
  • encoding 如果 string 是字符串,则这是它的字符编码。默认: 'utf8'
  • 返回: string 包含的字节数

返回一个字符串的实际字节长度。这与 String.prototype.length 不同,因为那返回字符串的字符数。

注意 对于 'base64''hex', 该函数假定有效的输入。 对于包含 non-Base64/Hex-encoded 数据的字符串 (e.g. 空格), 返回值可能大于从字符串中创建的 Buffer 的长度。

例子:

const str = '\u00bd + \u00bc = \u00be';// 输出: ½ + ¼ = ¾: 9 个字符, 12 个字节console.log(`${str}: ${str.length} 个字符, ` +            `${Buffer.byteLength(str, 'utf8')} 个字节`);

string 是一个 Buffer/DataView/TypedArray/ArrayBuffer/SharedArrayBuffer 时,返回实际的字节长度。

类方法:Buffer.compare(buf1, buf2)#

  • buf1 |
  • buf2 |
  • Returns:

比较 buf1buf2 ,通常用于 Buffer 实例数组的排序。相当于调用 buf1.compare(buf2)

例子:

const buf1 = Buffer.from('1234');const buf2 = Buffer.from('0123');const arr = [buf1, buf2];// 输出: [ ,  ]// (结果相当于: [buf2, buf1])console.log(arr.sort(Buffer.compare));

类方法:Buffer.concat(list[, totalLength])#

  • list 要合并的 BufferUint8Array 实例的数组
  • totalLength 合并时 listBuffer 实例的总长度
  • 返回:

返回一个合并了 list 中所有 Buffer 实例的新建的 Buffer

如果 list 中没有元素、或 totalLength 为 0 ,则返回一个新建的长度为 0 的 Buffer

如果没有提供 totalLength ,则从 list 中的 Buffer 实例计算得到。为了计算 totalLength 会导致需要执行额外的循环,所以提供明确的长度会运行更快。

如果提供了 totalLengthtotalLength 必须是一个正整数。如果从 list 中计算得到的 Buffer 长度超过了 totalLength,则合并的结果将会被截断为 totalLength 的长度。

例子:从一个包含三个 Buffer 实例的数组创建为一个单一的 Buffer

const buf1 = Buffer.alloc(10);const buf2 = Buffer.alloc(14);const buf3 = Buffer.alloc(18);const totalLength = buf1.length + buf2.length + buf3.length;// 输出: 42console.log(totalLength);const bufA = Buffer.concat([buf1, buf2, buf3], totalLength);// 输出: console.log(bufA);// 输出: 42console.log(bufA.length);

类方法:Buffer.from(array)#

  • array

通过一个八位字节的 array 创建一个新的 Buffer

例子:

// 创建一个新的包含字符串 'buffer' 的 UTF-8 字节的 Bufferconst buf = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]);

如果 array 不是一个数组,则抛出 TypeError 错误。

类方法:Buffer.from(arrayBuffer[, byteOffset[, length]])#

  • arrayBuffer | ArrayBufferSharedArrayBufferTypedArray.buffer 属性。
  • byteOffset 开始拷贝的索引。默认为 0
  • length 拷贝的字节数。默认为 arrayBuffer.length - byteOffset

该方法将创建一个 ArrayBuffer 的视图,而不会复制底层内存。例如,当传入一个 TypedArray 实例的 .buffer 属性的引用时,这个新建的 Buffer 会像 TypedArray 那样共享同一分配的内存。

例子:

const arr = new Uint16Array(2);arr[0] = 5000;arr[1] = 4000;// 与 `arr` 共享内存const buf = Buffer.from(arr.buffer);// 输出: console.log(buf);// 改变原始的 Uint16Array 也会改变 Bufferarr[1] = 6000;// 输出: console.log(buf);

可选的 byteOffsetlength 参数指定将与 Buffer 共享的 arrayBuffer 的内存范围。

例子:

const ab = new ArrayBuffer(10);const buf = Buffer.from(ab, 0, 2);// 输出: 2console.log(buf.length);

如果 arrayBuffer 不是 ArrayBufferSharedArrayBuffer,则抛出 TypeError 错误。

类方法:Buffer.from(buffer)#

  • buffer 一个要拷贝数据的已存在的 Buffer

将传入的 buffer 数据拷贝到一个新建的 Buffer 实例。

例子:

const buf1 = Buffer.from('buffer');const buf2 = Buffer.from(buf1);buf1[0] = 0x61;// 输出: aufferconsole.log(buf1.toString());// 输出: bufferconsole.log(buf2.toString());

如果 buffer 不是一个 Buffer,则抛出 TypeError 错误。

类方法:Buffer.from(string[, encoding])#

  • string 要编码的字符串
  • encoding string 的字符编码。 默认: 'utf8'

新建一个包含所给的 JavaScript 字符串 stringBufferencoding 参数指定 string 的字符编码。

例子:

const buf1 = Buffer.from('this is a tést');// 输出: this is a téstconsole.log(buf1.toString());// 输出: this is a tC)stconsole.log(buf1.toString('ascii'));const buf2 = Buffer.from('7468697320697320612074c3a97374', 'hex');// 输出: this is a téstconsole.log(buf2.toString());

如果 string 不是一个字符串,则抛出 TypeError 错误。

Class Method: Buffer.from(object[, offsetOrEncoding[, length]])#