Node.js Buffer解读

Posted Node前端

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Node.js Buffer解读相关的知识,希望对你有一定的参考价值。

Buffer是什么?

Buffer作为存在于全局对象上,无需引入模块即可使用,你绝对不可以忽略它。

可以理解Buffer是在内存中开辟的一片区域,用于存放二进制数据。Buffer所开辟的是堆外内存。

Buffer的应用场景有哪些?

怎么理解流呢?流是数据的集合(与数据、字符串类似),但是流的数据不能一次性获取到,数据也不会全部load到内存中,因此流非常适合大数据处理以及断断续续返回chunk的外部源。流的生产者与消费者之间的速度通常是不一致的,因此需要buffer来暂存一些数据。

buffer大小通过highWaterMark参数指定,默认情况下是16Kb。

存储需要占用大量内存的数据

Buffer 对象占用的内存空间是不计算在 Node.js 进程内存空间限制上的,所以可以用来存储大对象,但是对象的大小还是有限制的。一般情况下32位系统大约是1G,64位系统大约是2G。

如何创建Buffer

除了流自动隐式创建Buffer之外,也可以手动创建Buffer,方式如下:

Buffer中存储的数据已确定

 
   
   
 
  1. Buffer.from(obj)  // obj支持的类型string, buffer, arrayBuffer, array, or array-like object

注意:Buffer.from不支持传入数字,如下所示:

 
   
   
 
  1. Buffer.from(1234);

  2. buffer.js:208

  3.    throw new errors.TypeError(

  4.    ^

  5. TypeError [ERR_INVALID_ARG_TYPE]: The "value" argument must not be of type number. Received type number

  6.    at Function.from (buffer.js:208:11)

  7.    ...

  8. ```

  9. 若要传入数字可以采用传入数组的方式:

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

  11. console.log(buf); //  <Buffer 01 02 03 04>

但是这种方式存在一个问题,当存入不同的数值的时候buffer中记录的二进制数据会相同,如下所示:

 
   
   
 
  1. const buf2 = Buffer.from([127, -1]);

  2. console.log(buf2);     // <Buffer 7f ff>

  3. const buf3 = Buffer.from([127, 255]);

  4. console.log(buf3);    // <Buffer 7f ff>

  5. console.log(buf3.equals(buf2));  // true

当要记录的一组数全部落在0到255(readUInt8来读取)这个范围, 或者全部落在-128到127(readInt8来读取)这个范围那么就没有问题,

否则的话就强烈不推荐使用Buffer.from来保存一组数。因为不同的数字读取时应该调用不同的方法。

Buffer存储数据未确定

Buffer.alloc、Buffer.allocUnsafe、Buffer.allocUnsafeSlow

Buffer.alloc会用0值填充已分配的内存,所以相比后两者速度上要慢,但是也较为安全。当然也可以通过--zero-fill-buffers flag使allocUnsafe、allocUnsafeSlow在分配完内存后也进行0值填充。

 
   
   
 
  1. node --zero-fill-buffers index.js

当分配的空间小于4KB的时候,allocUnsafe会直接从之前预分配的Buffer里面slice空间,因此速度比allocUnsafeSlow要快,当大于等于4KB的时候二者速度相差无异。

 
   
   
 
  1. // 分配空间等于4KB

  2. function createBuffer(fn, size) {

  3.  console.time('buf-' + fn);

  4.  for (var i = 0; i < 100000; i++) {

  5.    Buffer[fn](size);

  6.  }

  7.  console.timeEnd('buf-' + fn);

  8. }

  9. createBuffer('alloc', 4096);

  10. createBuffer('allocUnsafe', 4096);

  11. createBuffer('allocUnsafeSlow', 4096);

  12. // 输出

  13. buf-alloc:           294.002ms

  14. buf-allocUnsafe:     224.072ms

  15. buf-allocUnsafeSlow: 209.22ms

  16. 复制代码function createBuffer(fn, size) {

  17.  console.time('buf-' + fn);

  18.  for (var i = 0; i < 100000; i++) {

  19.    Buffer[fn](size);

  20.  }

  21.  console.timeEnd('buf-' + fn);

  22. }

  23. createBuffer('alloc', 4095);

  24. createBuffer('allocUnsafe', 4095);

  25. createBuffer('allocUnsafeSlow', 4095);

  26. // 输出

  27. buf-alloc:           296.965ms

  28. buf-allocUnsafe:     135.877ms

  29. buf-allocUnsafeSlow: 205.225ms

  30. 复制代码需要谨记一点:new Buffer(xxxx) 方式已经不推荐使用了

  31. Buffer使用

  32. buffer转字符串

  33. const buf = Buffer.from('test');

  34. console.log(buf.toString('utf8'));                 // test

  35. console.log(buf.toString('utf8', 0, 2));           // te

  36. 复制代码bufferjson

  37. const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);

  38. console.log(buf.toJSON());    // { type: 'Buffer', data: [ 1, 2, 3, 4, 5 ] }

  39. 复制代码buffer裁剪,裁剪后返回的新的buffer与原buffer指向同一块内存

  40. buf.slice([start[, end]])

  41. start 起始位置

  42. end 结束位置(不包含)

` 示例:

 
   
   
 
  1. var buf1 = Buffer.from('test');

  2. var buf2 = buf1.slice(1, 3).fill('xx');

  3. console.log("buf2 content: " + buf2.toString()); // xx

  4. console.log("buf1 content: " + buf1.toString()); // txxt

  5. 复制代码buffer拷贝,buffer与数组不同,buffer的长度一旦确定就不再变化,因此当拷贝的源buffer比目标buffer大时只会复制部分的值

  6. buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])

示例:

 
   
   
 
  1. var buf1 = Buffer.from('abcdefghijkl');

  2. var buf2 = Buffer.from('ABCDEF');

  3. buf1.copy(buf2, 1);

  4. console.log(buf2.toString()); //Abcdef

  5. 复制代码buffer相等判断,比较的是二进制值

  6. buf.equals(otherBuffer)

示例:

 
   
   
 
  1. const buf1 = Buffer.from('ABC');

  2. const buf2 = Buffer.from('414243', 'hex');

  3. console.log(buf1.equals(buf2));    // true

  4. 复制代码除了equals之外,compare其实也可以用于判断是否相等(当结果为0则相等),不过compare更主要的作用是用于对数组内的buffer实例排序。

  5. buffer是否包含特定值

  6. buf.includes(value[, byteOffset][, encoding])

  7. buf.indexOf(value[, byteOffset][, encoding])

示例:

 
   
   
 
  1. const buf = Buffer.from('this is a buffer');

  2. console.log(buf.includes('this'));  // true

  3. console.log(buf.indexOf('this'));  // 0

写入读取数值

写入方法:

位数固定且超过1个字节的: write{Double| Float | Int16 | Int32| UInt16 | UInt32 }{BE|LE}(value, offset) 位数不固定的: write{Int | UInt}{BE | LE}(value, offset, bytelength) //此方法提供了更灵活的位数表示数据(比如3位、5位) 位数固定是1个字节的: write{Int8 | Unit8}(value, offset) 读取方法: 位数固定且超过1个字节的: read{Double| Float | Int16 | Int32 | UInt16 | UInt32 }{BE|LE}(offset) 位数不固定的: read{Int | UInt}{BE | LE}(offset, byteLength) 位数固定是1个字节的: read{Int8 | Unit8}(offset)

Double、Float、Int16、Int32、UInt16、UInt32既确定了表征数字的位数,也确定了是否包含负数,因此定义了不同的数据范围。同时由于表征数字的位数都超过8位,无法用一个字节来表示,因此就涉及到了计算机的字节序区分(大端字节序与小端字节序)

关于大端小端的区别可以这么理解:数值的高位在buffer的起始位置的是大端,数值的低位buffer的起始位置则是小端

 
   
   
 
  1. const buf = Buffer.allocUnsafe(2);

  2. buf.writeInt16BE(256, 0)  

  3. console.log(buf);           // <Buffer 01 00>

  4. buf.writeInt16LE(256, 0)

  5. console.log(buf);           // <Buffer 00 01>

https://tool.lu/hexconvert/ 这里可以查看数值的不同进制之间的转换,如果是大端的话,则直接按顺序(0100)拼接16进制即可,如果是小端则需要调换一下顺序才是正确的表示方式。

buffer合并

 
   
   
 
  1. Buffer.concat(list[, totalLength]) //totalLength不是必须的,如果不提供的话会为了计算totalLength会多一次遍历

  2. const buf1 = Buffer.from('this is');

  3. const buf2 = Buffer.from(' funny');

  4. console.log(Buffer.concat([buf1, buf2], buf1.length + buf2.length));

  5. // <Buffer 74 68 69 73 20 69 73 20 66 75 6e 6e 79>

清空buffer

清空buffer数据最快的办法是buffer.fill(0)

buffer模块与Buffer的关系

Buffer是全局global上的一个引用,指向的其实是buffer.Buffer

 
   
   
 
  1. const buffer = require('buffer');

  2. console.log(buffer.Buffer === Buffer); //true

buffer模块上还有其他一些属性和方法

 
   
   
 
  1. const buffer = require('buffer');

  2. console.log(buffer);

  3. { Buffer:

  4.   { [Function: Buffer]

  5.     poolSize: 8192,

  6.     from: [Function: from],

  7.     alloc: [Function: alloc],

  8.     allocUnsafe: [Function: allocUnsafe],

  9.     allocUnsafeSlow: [Function: allocUnsafeSlow],

  10.     isBuffer: [Function: isBuffer],

  11.     compare: [Function: compare],

  12.     isEncoding: [Function: isEncoding],

  13.     concat: [Function: concat],

  14.     byteLength: [Function: byteLength],

  15.     [Symbol(node.isEncoding)]: [Function: isEncoding] },

  16.  SlowBuffer: [Function: SlowBuffer],

  17.  transcode: [Function: transcode],

  18.  INSPECT_MAX_BYTES: 50,

  19.  kMaxLength: 2147483647,

  20.  kStringMaxLength: 1073741799,

  21.  constants: { MAX_LENGTH: 2147483647, MAX_STRING_LENGTH: 1073741799 } }

上面的kMaxLength与MAX_LENGTH代表了新建buffer时内存大小的最大值,当超过限制值后就会报错

32为机器上是(2^30)-1(~1GB) 64位机器上是(2^31)-1(~2GB)

Buffer释放

我们无法手动对buffer实例进行GC,只能依靠V8来进行,我们唯一能做的就是解除对buffer实例的引用

----







以上是关于Node.js Buffer解读的主要内容,如果未能解决你的问题,请参考以下文章

Node.js 7.0安装体验和示例代码解读

node js 小程序解密失败 乱码 Illegal Buffer

Node的文件系统及Buffer概述

Node.js Buffer

[Node.js]Buffer

Node.js JavaScript 片段中的跳过代码