Buffer 和二进制数据
更新: 10/16/2025 字数: 0 字 时长: 0 分钟
什么是 Buffer
Buffer 是 Node.js 中处理二进制数据的类,它在全局作用域中,不需要引入。
javascript
// Buffer 是全局对象
console.log(typeof Buffer) // 'function'
// 创建 Buffer
const buf = Buffer.from('Hello')
console.log(buf) // <Buffer 48 65 6c 6c 6f>为什么需要 Buffer
JavaScript 最初设计用于处理 UTF-16 字符串,不擅长处理二进制数据。Node.js 引入 Buffer 来处理:
- TCP 流
- 文件系统操作
- 图片处理
- 加密操作
- 等其他二进制数据
javascript
// 字符串 vs Buffer
const str = 'Hello'
const buf = Buffer.from(str)
console.log(str.length) // 5(字符数)
console.log(buf.length) // 5(字节数)
// 中文示例
const chinese = '你好'
const chineseBuf = Buffer.from(chinese)
console.log(chinese.length) // 2(字符数)
console.log(chineseBuf.length) // 6(UTF-8 编码字节数)创建 Buffer
1. Buffer.from()
从现有数据创建 Buffer。
javascript
// 从字符串创建
const buf1 = Buffer.from('Hello')
console.log(buf1) // <Buffer 48 65 6c 6c 6f>
// 指定编码
const buf2 = Buffer.from('Hello', 'utf8')
const buf3 = Buffer.from('48656c6c6f', 'hex')
// 从数组创建
const buf4 = Buffer.from([72, 101, 108, 108, 111])
console.log(buf4.toString()) // 'Hello'
// 从 ArrayBuffer 创建
const arr = new Uint8Array([72, 101, 108, 108, 111])
const buf5 = Buffer.from(arr.buffer)2. Buffer.alloc()
创建指定大小的 Buffer,并用 0 填充。
javascript
// 创建 10 字节的 Buffer
const buf = Buffer.alloc(10)
console.log(buf) // <Buffer 00 00 00 00 00 00 00 00 00 00>
// 创建并填充指定值
const buf2 = Buffer.alloc(5, 1)
console.log(buf2) // <Buffer 01 01 01 01 01>3. Buffer.allocUnsafe()
创建未初始化的 Buffer(性能更好,但可能包含旧数据)。
javascript
// ⚠️ 可能包含敏感的旧数据
const buf = Buffer.allocUnsafe(10)
console.log(buf) // <Buffer ... 可能是任意值 ...>
// 使用前应该填充
buf.fill(0)
console.log(buf) // <Buffer 00 00 00 00 00 00 00 00 00 00>4. 不推荐的方式
javascript
// ❌ 已弃用:不安全
const buf = new Buffer(10)
// ✅ 使用这些替代
const buf1 = Buffer.alloc(10)
const buf2 = Buffer.from('Hello')Buffer 编码
支持的编码类型
utf8: 默认编码utf16le/ucs2: UTF-16latin1/binary: Latin-1base64: Base64 编码hex: 十六进制ascii: ASCII
javascript
const str = 'Hello 你好'
// UTF-8(默认)
const buf1 = Buffer.from(str, 'utf8')
console.log(buf1.length) // 12 字节
// Base64
const buf2 = Buffer.from(str, 'utf8')
const base64 = buf2.toString('base64')
console.log(base64) // 'SGVsbG8g5L2g5aW9'
// Hex
const hex = buf2.toString('hex')
console.log(hex) // '48656c6c6f20e4bda0e5a5bd'
// 转回字符串
console.log(Buffer.from(base64, 'base64').toString()) // 'Hello 你好'
console.log(Buffer.from(hex, 'hex').toString()) // 'Hello 你好'读取和写入 Buffer
读取
javascript
const buf = Buffer.from([0x12, 0x34, 0x56, 0x78])
// 读取单个字节
console.log(buf[0]) // 18 (0x12)
console.log(buf.readUInt8(0)) // 18
// 读取 16 位整数
console.log(buf.readUInt16BE(0)) // 4660 (0x1234) Big-Endian
console.log(buf.readUInt16LE(0)) // 13330 (0x3412) Little-Endian
// 读取 32 位整数
console.log(buf.readUInt32BE(0)) // 305419896 (0x12345678)
console.log(buf.readUInt32LE(0)) // 2018915346 (0x78563412)写入
javascript
const buf = Buffer.alloc(4)
// 写入单个字节
buf[0] = 0x12
buf.writeUInt8(0x34, 1)
// 写入 16 位整数
buf.writeUInt16BE(0x5678, 2)
console.log(buf) // <Buffer 12 34 56 78>字节序(Endianness)
javascript
const buf = Buffer.allocUnsafe(4)
// Big-Endian (网络字节序)
// 高位字节存在低地址
buf.writeUInt32BE(0x12345678, 0)
console.log(buf) // <Buffer 12 34 56 78>
// Little-Endian (x86 架构)
// 低位字节存在低地址
buf.writeUInt32LE(0x12345678, 0)
console.log(buf) // <Buffer 78 56 34 12>Buffer 操作
1. 拼接
javascript
// 方式 1: Buffer.concat()
const buf1 = Buffer.from('Hello ')
const buf2 = Buffer.from('World')
const result = Buffer.concat([buf1, buf2])
console.log(result.toString()) // 'Hello World'
// 方式 2: 手动复制(性能更好)
const buf3 = Buffer.alloc(buf1.length + buf2.length)
buf1.copy(buf3, 0)
buf2.copy(buf3, buf1.length)
console.log(buf3.toString()) // 'Hello World'2. 切片
javascript
const buf = Buffer.from('Hello World')
// slice() 返回新 Buffer,但共享内存
const slice = buf.slice(0, 5)
console.log(slice.toString()) // 'Hello'
// 修改 slice 会影响原 Buffer
slice[0] = 0x68 // 'h'
console.log(buf.toString()) // 'hello World'
// subarray() 同 slice()(推荐使用)
const sub = buf.subarray(6)
console.log(sub.toString()) // 'World'3. 复制
javascript
const buf1 = Buffer.from('Hello')
const buf2 = Buffer.alloc(5)
// 复制到另一个 Buffer
buf1.copy(buf2)
console.log(buf2.toString()) // 'Hello'
// 部分复制
const buf3 = Buffer.alloc(10, '.')
buf1.copy(buf3, 2, 0, 3) // target, targetStart, sourceStart, sourceEnd
console.log(buf3.toString()) // '..Hel.....'4. 填充
javascript
const buf = Buffer.alloc(10)
// 填充单个字节
buf.fill(0xFF)
console.log(buf) // <Buffer ff ff ff ff ff ff ff ff ff ff>
// 填充字符串
buf.fill('abc')
console.log(buf.toString()) // 'abcabcabca'
// 部分填充
buf.fill('x', 2, 8)
console.log(buf.toString()) // 'abxxxxxxca'5. 比较
javascript
const buf1 = Buffer.from('ABC')
const buf2 = Buffer.from('ABD')
const buf3 = Buffer.from('ABC')
// compare() 返回 -1, 0, 或 1
console.log(buf1.compare(buf2)) // -1
console.log(buf1.compare(buf3)) // 0
console.log(buf2.compare(buf1)) // 1
// equals() 判断是否相等
console.log(buf1.equals(buf3)) // true
console.log(buf1.equals(buf2)) // false实战示例
1. 文件读写
javascript
const fs = require('fs')
// 读取二进制文件
const imageBuffer = fs.readFileSync('image.png')
console.log(imageBuffer.length) // 文件大小(字节)
// 检查文件类型(PNG 文件头)
if (imageBuffer[0] === 0x89 &&
imageBuffer[1] === 0x50 &&
imageBuffer[2] === 0x4E &&
imageBuffer[3] === 0x47) {
console.log('这是一个 PNG 文件')
}
// 写入二进制文件
fs.writeFileSync('copy.png', imageBuffer)2. Base64 图片编码
javascript
const fs = require('fs')
// 读取图片
const imageBuffer = fs.readFileSync('image.png')
// 转换为 Base64
const base64Image = imageBuffer.toString('base64')
const dataUrl = `data:image/png;base64,${base64Image}`
console.log(dataUrl)
// Base64 解码
const decoded = Buffer.from(base64Image, 'base64')
fs.writeFileSync('decoded.png', decoded)3. 加密和哈希
javascript
const crypto = require('crypto')
const data = 'Hello World'
// MD5 哈希
const md5 = crypto.createHash('md5')
.update(data)
.digest()
console.log(md5) // Buffer
console.log(md5.toString('hex')) // 十六进制字符串
// SHA256 哈希
const sha256 = crypto.createHash('sha256')
.update(data)
.digest('hex')
console.log(sha256)
// AES 加密
const algorithm = 'aes-256-cbc'
const key = crypto.randomBytes(32)
const iv = crypto.randomBytes(16)
const cipher = crypto.createCipheriv(algorithm, key, iv)
let encrypted = cipher.update(data, 'utf8', 'hex')
encrypted += cipher.final('hex')
console.log('加密:', encrypted)
// AES 解密
const decipher = crypto.createDecipheriv(algorithm, key, iv)
let decrypted = decipher.update(encrypted, 'hex', 'utf8')
decrypted += decipher.final('utf8')
console.log('解密:', decrypted)4. 网络数据包解析
javascript
// 解析简单的网络协议
function parsePacket(buffer) {
return {
version: buffer.readUInt8(0),
type: buffer.readUInt8(1),
length: buffer.readUInt16BE(2),
data: buffer.slice(4, 4 + buffer.readUInt16BE(2))
}
}
// 构建数据包
function buildPacket(version, type, data) {
const dataBuffer = Buffer.from(data)
const packet = Buffer.alloc(4 + dataBuffer.length)
packet.writeUInt8(version, 0)
packet.writeUInt8(type, 1)
packet.writeUInt16BE(dataBuffer.length, 2)
dataBuffer.copy(packet, 4)
return packet
}
// 使用
const packet = buildPacket(1, 2, 'Hello')
console.log(packet)
const parsed = parsePacket(packet)
console.log(parsed)5. 二进制协议通信
javascript
const net = require('net')
// 服务器
const server = net.createServer((socket) => {
socket.on('data', (buffer) => {
const command = buffer.readUInt8(0)
switch (command) {
case 0x01: // PING
const pong = Buffer.from([0x02]) // PONG
socket.write(pong)
break
case 0x03: // MESSAGE
const length = buffer.readUInt16BE(1)
const message = buffer.slice(3, 3 + length).toString()
console.log('收到消息:', message)
break
}
})
})
server.listen(8080)
// 客户端
const client = net.connect(8080, () => {
// 发送 PING
client.write(Buffer.from([0x01]))
// 发送消息
const message = 'Hello Server'
const buf = Buffer.alloc(3 + message.length)
buf.writeUInt8(0x03, 0)
buf.writeUInt16BE(message.length, 1)
buf.write(message, 3)
client.write(buf)
})性能优化
1. 预分配 Buffer
javascript
// ❌ 不好:频繁创建小 Buffer
function badConcat(buffers) {
let result = Buffer.alloc(0)
for (const buf of buffers) {
result = Buffer.concat([result, buf])
}
return result
}
// ✅ 好:预先计算总大小
function goodConcat(buffers) {
const totalLength = buffers.reduce((sum, buf) => sum + buf.length, 0)
const result = Buffer.allocUnsafe(totalLength)
let offset = 0
for (const buf of buffers) {
buf.copy(result, offset)
offset += buf.length
}
return result
}2. 使用 allocUnsafe
javascript
// 如果会立即写入数据,使用 allocUnsafe 更快
const buf = Buffer.allocUnsafe(1024)
buf.fill(0) // 需要时才清零
// 对比
console.time('alloc')
for (let i = 0; i < 10000; i++) {
Buffer.alloc(1024)
}
console.timeEnd('alloc') // 约 3ms
console.time('allocUnsafe')
for (let i = 0; i < 10000; i++) {
Buffer.allocUnsafe(1024)
}
console.timeEnd('allocUnsafe') // 约 1ms3. 缓冲池
javascript
class BufferPool {
constructor(blockSize = 8192) {
this.blockSize = blockSize
this.buffer = Buffer.allocUnsafe(blockSize)
this.offset = 0
}
alloc(size) {
if (size > this.blockSize) {
return Buffer.allocUnsafe(size)
}
if (this.offset + size > this.blockSize) {
this.buffer = Buffer.allocUnsafe(this.blockSize)
this.offset = 0
}
const buf = this.buffer.slice(this.offset, this.offset + size)
this.offset += size
return buf
}
}
const pool = new BufferPool()
const buf1 = pool.alloc(100)
const buf2 = pool.alloc(200)Buffer 与 TypedArray
Buffer 是 Uint8Array 的子类。
javascript
const buf = Buffer.from([1, 2, 3, 4])
// Buffer 是 Uint8Array 的实例
console.log(buf instanceof Uint8Array) // true
// 可以使用 TypedArray 的方法
console.log(buf.filter(x => x > 2)) // [3, 4]
console.log(buf.map(x => x * 2)) // [2, 4, 6, 8]
// 创建其他类型的视图
const view = new DataView(buf.buffer)
console.log(view.getUint16(0, true)) // 513 (Little-Endian)
// 创建不同类型的数组
const uint16 = new Uint16Array(buf.buffer)
console.log(uint16) // [513, 1027]内存管理
Buffer 的内存分配
javascript
// 小于 4KB 的 Buffer 使用共享内存池
const small = Buffer.alloc(1024)
// 大于等于 4KB 的 Buffer 直接分配
const large = Buffer.alloc(8192)
// 查看内存使用
console.log(process.memoryUsage())内存泄漏预防
javascript
// ❌ 可能导致内存泄漏
const leaks = []
setInterval(() => {
leaks.push(Buffer.alloc(1024 * 1024)) // 每次分配 1MB
}, 100)
// ✅ 限制缓存大小
const cache = []
const MAX_SIZE = 10
function addToCache(buf) {
cache.push(buf)
if (cache.length > MAX_SIZE) {
cache.shift() // 移除最旧的
}
}常见问题
1. Buffer 与字符串编码问题
javascript
// 中文字符在 UTF-8 中占 3 个字节
const str = '你好世界'
const buf = Buffer.from(str)
console.log(buf.length) // 12 (4 * 3)
// ⚠️ 截取可能导致乱码
console.log(buf.slice(0, 4).toString()) // '你�' (乱码)
// ✅ 正确的截取方式
const StringDecoder = require('string_decoder').StringDecoder
const decoder = new StringDecoder('utf8')
console.log(decoder.write(buf.slice(0, 4))) // '你'(自动处理不完整字符)2. Buffer 大小限制
javascript
// Buffer 最大大小受限于系统
// 32 位系统:约 1GB
// 64 位系统:约 2GB
try {
const huge = Buffer.alloc(2 ** 31) // 2GB
} catch (err) {
console.error('Buffer 太大:', err.message)
}总结
Buffer 的核心要点:
创建方式
Buffer.from(): 从现有数据创建Buffer.alloc(): 安全创建Buffer.allocUnsafe(): 快速创建
编码支持
- UTF-8, Base64, Hex, ASCII 等
性能优化
- 预分配 Buffer
- 使用 allocUnsafe
- 缓冲池
应用场景
- 文件操作
- 网络通信
- 加密解密
- 图片处理