概述
在现代网络应用中高效处理二进制数据是Node.js的核心能力,Buffer类则是实现这一能力的关键
Buffers: 使用比特、字节以及编码
1 ) 修改数据编码
Buffer 转换为其他格式
const fs = require('fs');
// 读取文件获取Buffer
fs.readFile('./txt/input.txt', (err, buf) => {
if (err) throw err;
// 检查是否为Buffer
if (Buffer.isBuffer(buf)) {
console.log('Data is a Buffer!');
// 转换为字符串
const content = buf.toString('utf8');
console.log('File content:', content);
}
});
toString方法详解: buf.toString([encoding[, start[, end]]])
- 支持的编码:‘utf8’、‘ascii’、‘base64’、‘hex’、'latin1’等
- 若不指定编码,默认使用’utf8’
- 可选参数start和end用于提取部分数据
- 使用Buffers修改字符串编码
字符串转Buffer示例
// 字符串转Buffer
const fs = require('fs');
// 字符串转Buffer
const authString = 'admin:secretpass';
const authBuffer = Buffer.from(authString);
// 转换为Base64
const base64Auth = authBuffer.toString('base64');
console.log('Authorization:', `Basic ${base64Auth}`);
// 处理Data URIs
const imageBuffer = fs.readFileSync('./img/monkey.png');
const dataURI = `data:image/png;base64,${imageBuffer.toString('base64')};`
console.log('Data URI:', dataURI.slice(0, 50) + '...'); // 截断输出
2 ) 二进制文件转换为JSON
处理DBASE5.0(.dbf)文件
const fs = require('fs');
function parseDBF(filePath) {
const buf = fs.readFileSync(filePath);
// 解析头部信息
const header = {
version: buf[0],
lastUpdate: new Date(1900 + buf[1], buf[2] - 1, buf[3]),
recordsCount: buf.readUInt32LE(4),
bytesInHeader: buf.readUInt16LE(8),
recordLength: buf.readUInt16LE(10)
};
// 解析字段描述
const fieldDescriptors = [];
let position = 32; // 字段描述起始位置
while (buf[position] !== 0x0D) {
const fieldName = buf.toString('ascii', position, position + 11).replace(/\0/g, '');
const fieldType = String.fromCharCode(buf[position + 11]);
fieldDescriptors.push({
name: fieldName,
type: fieldType,
length: buf[position + 16]
});
position += 32;
}
// 解析实际记录
const records = [];
position = header.bytesInHeader + 1; // 跳过头部结束符
for (let i = 0; i < header.recordsCount; i++) {
const record = {};
let deleted = buf[position] === 0x2A; // 星号表示删除
position++; // 跳过删除标记
for (const descriptor of fieldDescriptors) {
const value = buf.toString('utf8', position, position + descriptor.length).trim();
position += descriptor.length;
record[descriptor.name] = value;
}
if (!deleted) records.push(record);
}
return { header, fieldDescriptors, records };
}
const rs = parseDBF('./txt/world.dbf');
console.log(rs);
关键Buffer操作解析:
buf[0]:访问Buffer中的第一个字节buf.readUInt32LE(offset):从offset位置读取小端序32位无符号整数buf.readUInt16LE(offset):从offset位置读取小端序16位无符号整数buf.slice(start, end):创建指定字节范围的新Bufferbuf.toString(encoding, start, end):提取并转换指定范围的字节
3 ) 创建自定义二进制协议
简单的键值数据库协议实现
服务端 server.js
const zlib = require('zlib');
const net = require('net');
// 协议格式:| DB(1字节) | 操作(1字节) | 键长(2字节) | 键(n字节) | 值长(4字节) | 值(m字节) |
const server = net.createServer(socket => {
socket.on('data', data => {
// 准备读取数据的指针
let offset = 0;
// 读取数据库选择器 (1字节)
const db = data.readUInt8(offset);
offset += 1;
// 读取操作类型 (1字节)
const operation = data.readUInt8(offset);
offset += 1;
// 读取键长度 (2字节)
const keyLength = data.readUInt16BE(offset);
offset += 2;
// 读取键数据
const keyBuffer = data.slice(offset, offset + keyLength);
offset += keyLength;
const key = keyBuffer.toString('utf8');
let value = null;
if (operation === 1) { // 写操作
// 读取值长度 (4字节)
const valueLength = data.readUInt32BE(offset);
offset += 4;
// 读取压缩标记 (1字节)
const compressed = data.readUInt8(offset) === 1;
offset += 1;
// 读取值数据
let valueBuffer = data.slice(offset, offset + valueLength);
// 处理压缩
if (compressed) {
valueBuffer = zlib.inflateSync(valueBuffer);
}
value = valueBuffer.toString('utf8');
console.log(SET db=${db}, key=${key}, value=${value});
// 响应成功
const response = Buffer.alloc(1);
response.writeUInt8(1, 0); // 成功代码
socket.write(response);
}
else if (operation === 0) { // 读操作
// 模拟从数据库读取
const mockValue = Value for ${key} in db ${db};
const valueBuffer = Buffer.from(mockValue, 'utf8');
// 创建响应Buffer
const valueLength = valueBuffer.length;
const response = Buffer.alloc(4 + valueLength);
// 写入长度和数据
response.writeUInt32BE(valueLength, 0);
valueBuffer.copy(response, 4);
socket.write(response);
}
});
});
server.listen(3000, () => {
console.log('Binary protocol server listening on port 3000');
});
协议设计要点:
- 高效的数据表示:使用最少的字节表示元数据
- 压缩支持:对大值自动压缩减少传输量
- 类型安全:固定长度的字段确保可靠解析
- 扩展性:操作码支持未来添加新操作类型
客户端 client.js
const net = require('net');
const zlib = require('zlib');
// 构造协议数据包的工具函数
function createWritePacket(db, key, value, compress = false) {
const keyBuf = Buffer.from(key);
let valueBuf = Buffer.from(value);
const compressed = compress ? 1 : 0;
if (compress) {
valueBuf = zlib.deflateSync(valueBuf);
}
const packet = Buffer.alloc(1 + 1 + 2 + keyBuf.length + 4 + 1 + valueBuf.length);
let offset = 0;
packet.writeUInt8(db, offset); offset += 1; // DB
packet.writeUInt8(1, offset); offset += 1; // 操作码 (1=写)
packet.writeUInt16BE(keyBuf.length, offset); offset += 2; // 键长
keyBuf.copy(packet, offset); offset += keyBuf.length; // 键值
packet.writeUInt32BE(valueBuf.length, offset); offset += 4; // 值长
packet.writeUInt8(compressed, offset); offset += 1; // 压缩标记
valueBuf.copy(packet, offset); // 值数据
return packet;
}
function createReadPacket(db, key) {
const keyBuf = Buffer.from(key);
const packet = Buffer.alloc(1 + 1 + 2 + keyBuf.length);
let offset = 0;
packet.writeUInt8(db, offset); offset += 1; // DB
packet.writeUInt8(0, offset); offset += 1; // 操作码 (0=读)
packet.writeUInt16BE(keyBuf.length, offset); offset += 2; // 键长
keyBuf.copy(packet, offset); // 键值
return packet;
}
// 测试用例
function runTests() {
const tests = [
{
name: "正常写操作",
packet: createWritePacket(2, "username", "john_doe"),
validate: (data) => data.readUInt8(0) === 1
},
{
name: "压缩写操作",
packet: createWritePacket(3, "large_data", "a".repeat(1000), true),
validate: (data) => data.readUInt8(0) === 1
},
{
name: "正常读操作",
packet: createReadPacket(2, "username"),
validate: (data) => {
const len = data.readUInt32BE(0);
return data.slice(4).toString() === `Value for username in db 2`;
}
},
{
name: "错误操作码测试",
packet: Buffer.from([0, 5, 0, 3, 97, 98, 99]), // 操作码5无效
validate: (data) => data.length === 0 // 预期无响应
}
];
tests.forEach((test, index) => {
const client = net.connect({ port: 3000 }, () => {
client.write(test.packet);
});
client.on('data', (data) => {
const valid = test.validate(data);
console.log(`[TEST ${index+1}] ${test.name}: ${valid ? "✓ 通过" : "✗ 失败"}`);
client.end();
});
client.on('error', () => {
console.log(`[TEST ${index+1}] ${test.name}: ✗ 连接错误`);
});
});
}
// 执行测试
setTimeout(runTests, 1000); // 等待服务器启动
测试步骤
- 启动服务器
node server.js - 测试客户端
node client.js
输出
Binary protocol server listening on port 3000
SET db=2, key=username, value=john_doe
SET db=3, key=large_data, value=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
自定义二进制协议处理流程的可视化表示
4 ) Buffer核心总结
- 字符串与Buffer转换
Buffer.from(string[, encoding])字符串转Bufferbuf.toString([encoding])Buffer转字符串- 支持多种编码:UTF-8、Base64、Hex、ASCII等
- 二进制数据处理
- 直接访问特定字节:
buf[index] - 读取数字类型:
buf.readUInt8(offset)buf.readUInt16LE(offset)buf.readUInt32BE(offset)
- 创建子视图:
buf.slice(start, end)
- 协议设计与优化
- 掩码技术:使用位操作高效存储多个布尔标志
- 压缩集成:使用zlib压缩大数据减少传输量
- 内存管理:重用Buffer实例减少内存分配开销
- 字节序处理:正确处理LE/BE保证跨平台兼容性
- 常用API参考表
| 方法 | 描述 | 示例 |
|---|---|---|
Buffer.alloc(size) | 创建指定大小的Buffer | const buf = Buffer.alloc(10); |
Buffer.from(array) | 从数组创建Buffer | const buf = Buffer.from([0x62, 0x75]); |
buf.write(string, offset) | 写入字符串数据 | buf.write('Hello', 0); |
buf.readUIntLE(offset, byteLength) | 读取无符号小端整数 | const num = buf.readUIntLE(0, 4); |
buf.indexOf(value) | 查找值位置 | const pos = buf.indexOf('search'); |
buf.compare(target) | 比较两个Buffer | buf1.compare(buf2); |
Buffer.concat([buf1, buf2]) | 连接多个Buffer | const combined = Buffer.concat([p1, p2]); |
结语
Node.js的Buffer类提供了强大的二进制数据处理能力,是现代JavaScript开发中不可或缺的工具
无论是处理文件编码转换、解析二进制文件格式,还是设计高效的网络协议
Buffer API都是Node.js开发者必须掌握的核心技能
关键技术:
- Buffer与字符串间的编码转换技术
- 二进制文件格式的解析方法
- 自定义二进制协议的设计与实现
- Buffer核心API的实践应用
掌握这些技能将帮助您构建更高性能、更可靠的Node.js应用,特别是在处理网络通信、文件操作和数据序列化等关键领域
1255



被折叠的 条评论
为什么被折叠?



