在处理文件、网络请求、图片、音频等二进制数据时,JavaScript 在浏览器端往往依赖 Web API,而在后端环境中,Node.js 则提供了更底层、更高效的数据处理方式。Buffer 与 Stream 是 Node.js 中处理二进制数据的核心机制,它们构成了 Node.js 高性能 I/O 的基础。
本篇文章将从基础概念讲起,帮助你理解两者之间的关系、应用场景以及如何在实际项目中高效使用。
一、为什么 Node.js 需要 Buffer?
JavaScript 本身只提供字符串类型,没有原生的二进制数据结构。然而服务器程序需要处理的很多数据都不是文本,比如:
- 图片、视频、音频
- 文件读写
- 网络传输的二进制内容
- 压缩包、PDF 等
因此 Node.js 设计了 Buffer,它是一块专门用于存放二进制数据的内存区域,允许以更低层的方式与 I/O 系统交互。
示例:创建一个 Buffer 并写入数据。
js
const buf = Buffer.from("Hello");
console.log(buf); // <Buffer 48 65 6c 6c 6f>
打印出来的是十六进制表示的字节序列。
二、Buffer 的常用操作
1. 创建 Buffer
js
Buffer.from("文本内容"); // 从字符串创建
Buffer.alloc(10); // 分配固定大小的空 Buffer
Buffer.allocUnsafe(10); // 更快但可能含有旧数据
2. 读取和修改内容
Buffer 就像字节数组:
js
const buf = Buffer.from("ABC");
console.log(buf[0]); // 65
buf[0] = 97; // 修改为小写字母 a
3. Buffer 转字符串
js
buf.toString("utf-8");
Buffer 本质上是二进制与文本之间的桥梁。
三、什么是 Stream?
在处理大文件时,一次性把全部内容读入内存不仅低效,还可能导致崩溃。为了解决这个问题,Node.js 提供了 Stream(流):一种能够持续、分块处理数据的机制。
Stream 的核心特点:
- 不需一次性加载全部内容
- 内存使用更低
- 边读边处理,速度更快
- 适合大型文件处理、网络传输场景
例如:读取一个 2GB 的视频文件,使用 Stream 能够流式处理,而不是占用 2GB 内存。
四、Node.js Stream 的四种类型
-
Readable(可读流) 例如:文件读取、网络请求输入
-
Writable(可写流) 如:文件写入、网络发送
-
Duplex(双工流) 既可读又可写,如:TCP 连接
-
Transform(转换流) 在读写过程中处理数据,如:压缩、加密
Stream 是 Node.js 快速处理 I/O 的核心工具。
五、使用 Stream 读取文件
这是最常见的应用场景之一。
js
const fs = require("fs");
const rs = fs.createReadStream("input.txt", { encoding: "utf-8" });
rs.on("data", chunk => {
console.log("读取到:", chunk);
});
rs.on("end", () => {
console.log("读取完成");
});
文件会被分成多个小段按顺序读取,不会占用大量内存。
六、使用 Stream 写入文件
js
const ws = fs.createWriteStream("output.txt");
ws.write("第一行\n");
ws.write("第二行\n");
ws.end("写入结束");
写入流同样是分块写入,避免大文件造成卡顿。
七、流的强大连接方式:pipe()
Stream 最典型的用法就是 管道(pipe)。你可以将一个可读流直接"输送"到可写流。
例如:复制文件
js
fs.createReadStream("input.mp4")
.pipe(fs.createWriteStream("copy.mp4"));
只需一行代码,就能实现高效的数据传输,且非常节省内存。
八、Buffer 与 Stream 的关系
两者并非互相对立,而是密不可分:
- Stream 负责"流式分段处理"数据
- Buffer 负责"存储每一段的二进制数据"
Stream 就像水流,Buffer 就像每一杯盛水的容器。 每次 Stream 分发的数据 chunk 本质上就是一个 Buffer。
例如:
js
rs.on("data", chunk => {
console.log(Buffer.isBuffer(chunk)); // true
});
理解这一点有助于你正确处理大数据流。
九、使用 Buffer 处理网络与二进制协议
在 TCP 通信、WebSocket、图像处理时,你会频繁遇到 Buffer。例如:
js
socket.on("data", chunk => {
// chunk 是 Buffer
});
很多底层协议都会用 Buffer 来解析消息结构,如获取长度、类型、内容等。
十、总结
Buffer 与 Stream 是 Node.js 面向 I/O 设计的两大关键机制:
- Buffer 提供了操作二进制的能力,是文件、网络、加密等场景的基础。
- Stream 提供了高效处理大规模数据的方式,使 Node.js 能轻松应对大型文件、实时传输等需求。
掌握它们不仅能写出更高性能的程序,还能帮助你理解 Node.js 在底层如何处理数据流动,为后续学习网络模块、文件系统与 HTTP 服务打下扎实基础。