0

0

关于Node.js中Buffer的一些被忽略的用法详解

黄舟

黄舟

发布时间:2017-03-28 14:23:53

|

1765人浏览过

|

来源于php中文网

原创

网上关于node.js中buffer用法的文章有很多,但是感觉还是不够详细,所以这篇文章主要介绍了关于node.js中buffer的一些你可能不知道的用法,文中介绍的非常详细,需要的朋友可以参考借鉴,下面来一起看看吧。

前言

在大多数介绍 Buffer 的文章中,主要是围绕数据拼接和内存分配这两方面的。比如我们使用fs模块来读取文件内容的时候,返回的就是一个 Buffer:

fs.readFile('filename', function (err, buf) {
 // 
});

在使用net或http模块来接收网络数据时,data事件的参数也是一个 Buffer,这时我们还需要使用Buffer.concat()来做数据拼接:

var bufs = [];
conn.on('data', function (buf) {
 bufs.push(buf);
});
conn.on('end', function () {
 // 接收数据结束后,拼接所有收到的 Buffer 对象
 var buf = Buffer.concat(bufs);
});

还可以利用Buffer.toString()来做转换base64或十六进制字符的转换,比如:

console.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!

一般情况下,单个 Node.js 进程是有最大内存限制的,以下是来自官方文档中的说明:

What is the memory limit on a node process?

Currently, by default v8 has a memory limit of 512MB on 32-bit systems, and 1.4GB on 64-bit systems. The limit can be raised by setting --max_old_space_size to a maximum of ~1024 (~1 GB) (32-bit) and ~4096 (~4GB) (64-bit), but it is recommended that you split your single process into several workers if you are hitting memory limits.

由于 Buffer 对象占用的内存空间是不计算在 Node.js 进程内存空间限制上的,因此,我们也常常会使用 Buffer 来存储需要占用大量内存的数据:

// 分配一个 2G-1 字节的数据
// 单次分配内存超过此值会抛出异常 RangeError: Invalid typed array length
var buf = new Buffer(1024 * 1024 * 1024 - 1);

以上便是 Buffer 的几种常见用法。然而,阅读 Buffer 的 API 文档时,我们会发现更多的是readXXX()writeXXX()开头的 API,具体如下:

  • buf.readUIntLE(offset, byteLength[, noAssert])

  • buf.readUIntBE(offset, byteLength[, noAssert])

  • buf.readIntLE(offset, byteLength[, noAssert])

  • buf.readIntBE(offset, byteLength[, noAssert])

  • buf.readUInt8(offset[, noAssert])

  • buf.readUInt16LE(offset[, noAssert])

  • buf.readUInt16BE(offset[, noAssert])

  • buf.readUInt32LE(offset[, noAssert])

  • buf.readUInt32BE(offset[, noAssert])

  • buf.readInt8(offset[, noAssert])

  • buf.readInt16LE(offset[, noAssert])

  • buf.readInt16BE(offset[, noAssert])

  • buf.readInt32LE(offset[, noAssert])

  • buf.readInt32BE(offset[, noAssert])

  • buf.readFloatLE(offset[, noAssert])

  • buf.readFloatBE(offset[, noAssert])

  • buf.readDoubleLE(offset[, noAssert])

  • buf.readDoubleBE(offset[, noAssert])

  • buf.write(string[, offset][, length][, encoding])

  • buf.writeUIntLE(value, offset, byteLength[, noAssert])

  • buf.writeUIntBE(value, offset, byteLength[, noAssert])

  • buf.writeIntLE(value, offset, byteLength[, noAssert])

  • buf.writeIntBE(value, offset, byteLength[, noAssert])

    酷表ChatExcel
    酷表ChatExcel

    北大团队开发的通过聊天来操作Excel表格的AI工具

    下载
  • buf.writeUInt8(value, offset[, noAssert])

  • buf.writeUInt16LE(value, offset[, noAssert])

  • buf.writeUInt16BE(value, offset[, noAssert])

  • buf.writeUInt32LE(value, offset[, noAssert])

  • buf.writeUInt32BE(value, offset[, noAssert])

  • buf.writeInt8(value, offset[, noAssert])

  • buf.writeInt16LE(value, offset[, noAssert])

  • buf.writeInt16BE(value, offset[, noAssert])

  • buf.writeInt32LE(value, offset[, noAssert])

  • buf.writeInt32BE(value, offset[, noAssert])

  • buf.writeFloatLE(value, offset[, noAssert])

  • buf.writeFloatBE(value, offset[, noAssert])

  • buf.writeDoubleLE(value, offset[, noAssert])

  • buf.writeDoubleBE(value, offset[, noAssert])

这些 API 为在 Node.js 中操作数据提供了极大的便利。假设我们要将一个整形数值存储到文件中,比如当前时间戳为1447656645380,如果将其当作一个字符串存储时,需要占用 11 字节的空间,而将其转换为二进制存储时仅需 6 字节空间即可:

var buf = new Buffer(6);

buf.writeUIntBE(1447656645380, 0, 6);
// 

buf.readUIntBE(0, 6);
// 1447656645380

在使用 Node.js 编写一些底层功能时,比如一个网络通信模块、某个数据库的客户端模块,或者需要从文件中操作大量结构化数据时,以上 Buffer 对象提供的 API 都是必不可少的。

接下来将演示一个使用 Buffer 对象操作结构化数据的例子。

操作结构化数据

假设有一个学生考试成绩数据库,每条记录结构如下:

学号 课程代码 分数
XXXXXX XXXX XX

其中学号是一个 6 位的数字,课程代码是一个 4 位数字,分数最高分为 100 分。

在使用文本来存储这些数据时,比如使用 CSV 格式存储可能是这样的:

100001,1001,99
100002,1001,67
100003,1001,88

其中每条记录占用 15 字节的空间,而使用二进制存储时其结构将会是这样:

学号 课程代码 分数
3 字节 2 字节 1 字节

每一条记录仅需要 6 字节的空间即可,仅仅是使用文本存储的 40%!下面是用来操作这些记录的程序:

// 读取一条记录
// buf Buffer 对象
// offset 本条记录在 Buffer 对象的开始位置
// data {number, lesson, score}
function writeRecord (buf, offset, data) {
 buf.writeUIntBE(data.number, offset, 3);
 buf.writeUInt16BE(data.lesson, offset + 3);
 buf.writeInt8(data.score, offset + 5);
}

// 写入一条记录
// buf Buffer 对象
// offset 本条记录在 Buffer 对象的开始位置
function readRecord (buf, offset) {
 return {
 number: buf.readUIntBE(offset, 3),
 lesson: buf.readUInt16BE(offset + 3),
 score: buf.readInt8(offset + 5)
 };
}

// 写入记录列表
// list 记录列表,每一条包含 {number, lesson, score}
function writeList (list) {
 var buf = new Buffer(list.length * 6);
 var offset = 0;
 for (var i = 0; i < list.length; i++) {
 writeRecord(buf, offset, list[i]);
 offset += 6;
 }
 return buf;
}

// 读取记录列表
// buf Buffer 对象
function readList (buf) {
 var offset = 0;
 var list = [];
 while (offset < buf.length) {
 list.push(readRecord(buf, offset));
 offset += 6;
 }
 return list;
}

我们可以再编写一段程序来看看效果:

var list = [
 {number: 100001, lesson: 1001, score: 99},
 {number: 100002, lesson: 1001, score: 88},
 {number: 100003, lesson: 1001, score: 77},
 {number: 100004, lesson: 1001, score: 66},
 {number: 100005, lesson: 1001, score: 55},
];
console.log(list);

var buf = writeList(list);
console.log(buf);
// 输出 

var ret = readList(buf);
console.log(ret);
/* 输出
[ { number: 100001, lesson: 1001, score: 99 },
 { number: 100002, lesson: 1001, score: 88 },
 { number: 100003, lesson: 1001, score: 77 },
 { number: 100004, lesson: 1001, score: 66 },
 { number: 100005, lesson: 1001, score: 55 } ]
*/

lei-proto 模块介绍

上面的例子中,当每一条记录的结构有变化时,我们需要修改readRecord()writeRecord() ,重新计算每一个字段在 Buffer 中的偏移量,当记录的字段比较复杂时很容易出错。为此我编写了lei-proto模块,它允许你通过简单定义每条记录的结构即可生成对应的readRecord()`writeRecord()函数。

首先执行以下命令安装此模块:

$ npm install lei-proto --save

使用lei-proto模块后,前文的例子可以改为这样:

var parsePorto = require('lei-proto');

// 生成指定记录结构的数据编码/解码器
var record = parsePorto([
 ['number', 'uint', 3],
 ['lesson', 'uint', 2],
 ['score', 'uint', 1]
]);

function readList (buf) {
 var list = [];
 var offset = 0;
 while (offset < buf.length) {
 list.push(record.decode(buf.slice(offset, offset + 6)));
 offset += 6;
 }
 return list;
}

function writeList (list) {
 return Buffer.concat(list.map(record.encodeEx));
}

运行与上文同样的测试程序,可看到其结果是一样的:


[ { number: 100001, lesson: 1001, score: 99 },
 { number: 100002, lesson: 1001, score: 88 },
 { number: 100003, lesson: 1001, score: 77 },
 { number: 100004, lesson: 1001, score: 66 },
 { number: 100005, lesson: 1001, score: 55 } ]

总结

相关专题

更多
高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

4

2026.01.16

全民K歌得高分教程大全
全民K歌得高分教程大全

本专题整合了全民K歌得高分技巧汇总,阅读专题下面的文章了解更多详细内容。

3

2026.01.16

C++ 单元测试与代码质量保障
C++ 单元测试与代码质量保障

本专题系统讲解 C++ 在单元测试与代码质量保障方面的实战方法,包括测试驱动开发理念、Google Test/Google Mock 的使用、测试用例设计、边界条件验证、持续集成中的自动化测试流程,以及常见代码质量问题的发现与修复。通过工程化示例,帮助开发者建立 可测试、可维护、高质量的 C++ 项目体系。

10

2026.01.16

java数据库连接教程大全
java数据库连接教程大全

本专题整合了java数据库连接相关教程,阅读专题下面的文章了解更多详细内容。

33

2026.01.15

Java音频处理教程汇总
Java音频处理教程汇总

本专题整合了java音频处理教程大全,阅读专题下面的文章了解更多详细内容。

15

2026.01.15

windows查看wifi密码教程大全
windows查看wifi密码教程大全

本专题整合了windows查看wifi密码教程大全,阅读专题下面的文章了解更多详细内容。

42

2026.01.15

浏览器缓存清理方法汇总
浏览器缓存清理方法汇总

本专题整合了浏览器缓存清理教程汇总,阅读专题下面的文章了解更多详细内容。

7

2026.01.15

ps图片相关教程汇总
ps图片相关教程汇总

本专题整合了ps图片设置相关教程合集,阅读专题下面的文章了解更多详细内容。

9

2026.01.15

ppt一键生成相关合集
ppt一键生成相关合集

本专题整合了ppt一键生成相关教程汇总,阅读专题下面的的文章了解更多详细内容。

6

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Node.js 教程
Node.js 教程

共57课时 | 8.7万人学习

【web前端】Node.js快速入门
【web前端】Node.js快速入门

共16课时 | 2万人学习

Node.js-前端工程化必学
Node.js-前端工程化必学

共19课时 | 3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号