Javascript 在客户端对于 unicode 编码的数据操作支持非常友好,但是对二进制数据的处理就不尽人意。Node.js 为了能够处理二进制数据或非 unicode 编码的数据,便设计了 Buffer 类,该类实现了 Uint8Array 接口,并对其进行了优化,它的实例类似于整型数组,但是它的大小在创建后便不可调整。在介绍 Buffer 如何使用之前,先介绍几个知识点。
V8 引擎最大堆内存使用在 32 位系统上默认为 512M,在 64 位系统上是 1GB,虽然可以使用
参数调整该值,但还是建议要用到大内存的时候使用 Buffer 或 Stream,因为 Buffer 的内存分配不在 V8 的堆上。
- --max-old-space-size
单个 Buffer 实例的大小最大数值为 1GB-1(32 位系统)或 2GB-1(64 位系统),所以在创建 Buffer 实例的时候不能超过该值,或者使用
方法读取大文件,否则将抛出 RangeError 错误。
- readFile()
Nodejs 在创建 Buffer 实例的时候,当用户申请的空间大于 8KB,会直接调用内部的
方法创建一个 Buffer,如果申请的空间大于 0 且小于 4KB,新的 Buffer 则会建立在当前的 8kb SLAB 上,并更新剩余空间,如下图所示:
- createUnsafeBuffer()
下面介绍 Buffer API 的简单使用:
1、创建 Buffer 实例
使用
等方法来创建一个 Buffer 实例,6.0 版本以前直接使用构造函数创建的方法
- Buffer.from(), Buffer.alloc(), Buffer.allocUnsafe()
已被丢弃,不推荐使用,因为有可能会造成内存泄漏。
- new Buffer()
,参数含义如下:
- Buffer.alloc(size[, fill[, encoding]])
使用如下所示:
- const buf1 = Buffer.alloc(10);
- console.log(buf1); //<Buffer 00 00 00 00 00 00 00 00 00 00>
- const buf2 = Buffer.alloc(10, 'hello');
- console.log(buf2); //<Buffer 68 65 6c 6c 6f 68 65 6c 6c 6f>
- const buf3 = Buffer.alloc(10, 'hello', 'base64');
- console.log(buf3); //<Buffer 85 e9 65 85 e9 65 85 e9 65 85>
方法
,size 参数指定 buffer 的大小,该方法返回一个没有初始化的 buffer,因此可能还保留有敏感的数据,造成信息的泄漏,建议使用
- Buffer.allocUnsafe(size)
函数初始化 buffer,该方法与 Buffer.alloc(size, fill) 是不一样的,有可能使用 8KB 池。使用如下所示:
- buffer.fill(0)
- const buf4 = Buffer.allocUnsafe(10);
- console.log(buf4); //<Buffer 68 fb 4d 00 00 00 00 00 08 00>,可以看出是有数据的
- buf4.fill(0);
- console.log(buf4); //<Buffer 00 00 00 00 00 00 00 00 00 00>
方法
,参数含义同上,该方法不会使用 Buffer 池,容易造成内存的浪费,使用如下所示:
- Buffer.allocUnsafeSlow(size)
- const buf5 = Buffer.allocUnsafeSlow(10);
- console.log(buf5); //<Buffer 38 00 24 00 00 00 00 00 00 00>
方法
,这里分为四种情况,如下所示: 第一,value 为 16 进制数组,将数组转化为 buffer,如果不是 16 进制,则会进行转换,如下:
- Buffer.from(value,[...])
- const buf6 = Buffer.from([1, 2, 3, 5, 17]);
- console.log(buf6); //<Buffer 01 02 03 05 11>
第二,value 为字符串,则转换字符串为 buffer,该方法会使用 buffer 池,如下:
- const buf7 = Buffer.from('hello world!');
- console.log(buf7); //<Buffer 01 02 03 05 11>
第三,value 为 buffer 实例,则将 value 拷贝至新的 buffer 中,这里只是值的拷贝,不会共享内存,如下:
- const buf8 = Buffer.from('hello world');
- const buf9 = Buffer.from(buf8);
- console.log(buf8); //<Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
- console.log(buf9); //<Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
- buf9[0] = 0x66;
- console.log(buf8); //<Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
- console.log(buf9); //<Buffer 66 65 6c 6c 6f 20 77 6f 72 6c 64>
第四,value 为 arrayBuffer 时,还有两个可选参数 [, byteOffset[, length]],byteOffset 指定从 arrayBuffer 开始复制的位置,length 复制的长度。如下:
- const arr = new Uint8Array(2);
- arr[0] = 128;
- arr[1] = 200;
- const buf10 = Buffer.from(arr, 0, 2);
- console.log(buf10); //<Buffer 80 c8>
如果引用的是 arr.buffer,则新创建的 buffer buf10 与 arr 共享内存,如下:
- const arr = new Uint8Array(2);
- arr[0] = 128;
- arr[1] = 200;
- const buf10 = Buffer.from(arr.buffer);
- arr[0] = 254;
- console.log(buf10); //<Buffer fe c8>
2、buffer 解码
使用
方法将 buffer 转换成字符串,encoding 指定字符编码,默认为'utf8',start 开始位置,end 结束位置(不包括),目前 encoding 只支持'ascii,utf8,utf16le,ucs2,base64,latin1,binary,hex',使用如下所示:
- buf.toString([encoding[, start[, end]]])
- const buf12 = Buffer.from('我爱中国');
- console.log(buf12.toString('base64')); //5oiR54ix5Lit5Zu9
- console.log(buf12.toString('utf8')); //我爱中国
- console.log(buf12.toString('hex')); //e68891e788b1e4b8ade59bbd
3、buffer 拼接、复制、填充、分割
方法
使用指定的值填充 buffer,参数 offset 指定填充的起始位置,end 为结束位置,使用如下所示:
- buf.fill(value[, offset[, end]][, encoding])
- console.log(Buffer.allocUnsafe(5).fill('a').toString()); //aaaaa
- console.log(Buffer.allocUnsafe(5).fill(65).toString('utf8')); //AAAAA
方法
将多个 buffer 合并在一起,并返回一个新的 buffer 实例,参数 totalLength 为指定的 buffers 的长度总和,如果不提供该值,函数内部会循环去获取每一个 buffer 的长度,然后进行拼接,因此为了速度,最好指定一个总长度,使用如下:
- Buffer.concat(list[, totalLength])
- function bufferInjoin(buffArr) {
- var len = 0;
- buffArr.forEach((buff, idx, arr) = >{
- len += buff.length;
- });
- var buffer = Buffer.concat(buffArr, len);
- return buffer;
- }
- var buff = bufferInjoin([Buffer.from('hehe'), Buffer.allocUnsafe(5).fill('a')]);
- console.log(buff); //<Buffer 68 65 68 65 61 61 61 61 61>
- console.log(buff.length); //9
- console.log(buff.toString()); //heheaaaaa
方法
可以实现 buf 到 target 的复制,参数含义如下:
- buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])
方法
- const buf1 = Buffer.from('hello world!');
- const buf2 = Buffer.allocUnsafe(5).fill('x');
- buf1.copy(buf2, 0, 0, 5);
- console.log(buf2.toString()); //hello
可以分割 buffer,返回一个新的 buffer,但是仍然是引用原 buffer,因此改变原 buffer 数据,该新 buffer 也会跟着改变,如果参数 start,end 为负数,则先要加上 buffer 的长度再进行计算,如下所示:
- buf.slice([start[, end]])
3、buffer 读写
- const buf1 = Buffer.from('hello world.');
- const buf2 = buf1.slice(0);
- console.log(buf2); //<Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64 2e>
- buf2[0] = 88;
- console.log(buf1); //<Buffer 58 65 6c 6c 6f 20 77 6f 72 6c 64 2e>
- const buf3 = buf1.slice( - 6, -1);
- console.log(buf3.toString()); //world
buffer 读操作由 read 开头的 api 完成,主要有以下这些:
使用如下所示, 以 32 无符号整型为例:
- const buf = Buffer.allocUnsafe(8);
- buf.writeUInt32BE(0x12345678, 0) console.log(buf);
- const data = buf.readUInt32BE(0);
- console.log(data.toString(16));
最后利用 buffer 读 API 完成一个获取 PNG 格式图片尺寸的小工具,在开始编码之前,先简单介绍下 PNG 文件组成,如下所示:
PNG 文件标志 | PNG 数据块 | …… | PNG 数据块 |
---|
这里我们只要用到 PNG 文件标识和 PNG 数据块的第一个块 IHDR 文件头数据块。文件标识是固定的 8 个字节,为
,IHDR 数据块的长度为 13 个字节,格式如下:
- 89 50 4E 47 0D 0A 1A 0A
域的名称 | 字节数 | 说明 |
---|---|---|
Width | 4 bytes | 宽度 |
Height | 4 bytes | 高度 |
Bit depth | 1 bytes | 图像深度 |
ColorType | 1 bytes | 颜色类型 |
Compression method | 1 bytes | 压缩方法 |
Filter method | 1 bytes | 滤波器方法 |
Interlace method | 1 bytes | 隔行扫描方法 |
开始编码,如下所示:
- const fs = require('fs');
- const path = require('path');
- const argvs = process.argv.slice(2);
- if (argvs.length <= 0) {
- console.error('请输入图片:png.js img1 img2 ...');
- process.exit( - 1);
- }
- argvs.forEach((img, idx, arr) = >{
- var stat = fs.statSync(img);
- fs.open(img, 'r', (err, fd) = >{
- if (err) throw err;
- var buff = Buffer.alloc(stat.size);
- fs.read(fd, buff, 0, stat.size, 0, (err, bytesRead, buffer) = >{
- if (err) throw err;
- fs.close(fd, () = >{});
- getImgDimension(buff, (err, dimension) = >{
- if (err) throw err;
- console.log(`$ {
- img
- }的尺寸为:$ {
- dimension.width
- }
- x$ {
- dimension.height
- }`);
- });
- });
- });
- });
- function getImgDimension(buff, cb) {
- if ((buff.toString('utf8', 1, 8) === 'PNG\r\n\x1a\n') && (buff.toString('utf8', 12, 16) === 'IHDR')) {
- return cb(null, {
- width: buff.readUInt32BE(16),
- height: buff.readUInt32BE(20)
- }),
- !0;
- } else {
- return cb(new Error('不是PNG图片'), {}),
- !1;
- }
- }
执行结果如下:
来源: http://www.cnblogs.com/zmxmumu/p/6124960.html