"Nếu phải đọc một file 20 GB, bạn sẽ tải toàn bộ vào RAM hay đọc từng phần nhỏ? Câu trả lời chính là Buffer và Stream - hai nền tảng giúp Node.js xử lý dữ liệu cực kỳ hiệu quả."
🎯 Mục tiêu bài học
Sau bài này bạn sẽ:
Hiểu Buffer là gì.
Hiểu Stream là gì.
Biết các loại Stream trong Node.js.
Thành thạo Readable Stream và Writable Stream.
Biết sử dụng
pipe().Hiểu vì sao Stream giúp Node.js xử lý file lớn với hiệu năng cao.
Buffer là gì?
Máy tính chỉ hiểu dữ liệu dưới dạng byte.
Node.js sử dụng Buffer để lưu trữ dữ liệu nhị phân (Binary Data).
Ví dụ:
File ảnh
Video
Âm thanh
PDF
File ZIP
Dữ liệu truyền qua mạng
đều được xử lý bằng Buffer.
Tạo Buffer
const buf =
Buffer.from("Hello");
In ra
console.log(buf);
↓
<Buffer 48 65 6c 6c 6f>
Đây chính là mã ASCII của chuỗi.
Đọc nội dung Buffer
console.log(
buf.toString()
);
↓
Hello
Buffer từ mảng số
const buf =
Buffer.from([
65,
66,
67
]);
console.log(
buf.toString()
);
↓
ABC
Buffer.alloc()
Tạo Buffer có kích thước cố định.
const buf =
Buffer.alloc(8);
console.log(buf);
↓
<Buffer 00 00 00 00 00 00 00 00>
Buffer.byteLength()
Đếm số byte.
console.log(
Buffer.byteLength(
"Node.js"
)
);
↓
7
Ví dụ với Unicode
console.log(
Buffer.byteLength(
"Xin chào"
)
);
Kết quả sẽ lớn hơn số ký tự vì một số ký tự Unicode chiếm nhiều hơn 1 byte.
Stream là gì?
Thay vì:
Đọc toàn bộ file
↓
Đưa vào RAM
↓
Xử lý
Node.js có thể:
Đọc từng phần nhỏ
↓
Xử lý
↓
Đọc tiếp
Đó chính là Stream.
Ví dụ
File
movie.mp4
Dung lượng
20 GB
Nếu đọc bằng:
readFile()
↓
RAM phải chứa toàn bộ file.
Nếu dùng Stream
64 KB
↓
64 KB
↓
64 KB
↓
...
Node chỉ giữ một phần dữ liệu trong bộ nhớ tại mỗi thời điểm.
Lợi ích của Stream
Tiết kiệm RAM.
Tăng tốc xử lý.
Phù hợp file lớn.
Có thể xử lý dữ liệu ngay khi nhận được.
Được sử dụng trong HTTP, Upload và Download.
Các loại Stream
Node.js có bốn loại Stream.
| Loại | Ý nghĩa |
|---|---|
| Readable | Đọc dữ liệu |
| Writable | Ghi dữ liệu |
| Duplex | Đọc và ghi |
| Transform | Đọc, biến đổi rồi ghi |
Readable Stream
Đọc file.
import fs from "node:fs";
const stream =
fs.createReadStream(
"data.txt", {
highWaterMark: 5
}
);
Lắng nghe dữ liệu
stream.on(
"data",
(chunk)=>{
console.log(chunk);
});
↓
<Buffer ...>
Mỗi lần sẽ nhận một chunk (khối dữ liệu).
Đọc thành chuỗi
stream.setEncoding(
"utf8"
);
stream.on(
"data",
(chunk)=>{
console.log(chunk);
});
↓
Xin
chào
Node.js
Tùy kích thước file, dữ liệu có thể được chia thành nhiều chunk.
Khi đọc xong
stream.on(
"end",
()=>{
console.log(
"Hoàn thành"
);
});
↓
Hoàn thành
Xử lý lỗi
stream.on(
"error",
(err)=>{
console.log(err);
});
Ví dụ:
ENOENT
Writable Stream
Tạo Stream ghi.
import fs from "node:fs";
const stream =
fs.createWriteStream(
"log.txt"
);
Ghi
stream.write(
"Hello\n"
);
stream.write(
"Node.js\n"
);
stream.end();
Nội dung file
Hello
Node.js
end()
Kết thúc Stream.
stream.end();
Sau khi gọi end(), bạn không nên tiếp tục ghi thêm dữ liệu vào Stream đó.
pipe()
Đây là phương pháp phổ biến nhất.
Ví dụ sao chép file (phải có file video.mp4 trong thư mục gốc).
import fs from "node:fs";
const read =
fs.createReadStream(
"video.mp4"
);
const write =
fs.createWriteStream(
"copy.mp4"
);
read.pipe(write);
Sơ đồ
video.mp4
↓
Readable
↓
pipe()
↓
Writable
↓
copy.mp4
Không cần đọc toàn bộ file vào RAM.
highWaterMark
Xác định kích thước mỗi chunk.
const stream =
fs.createReadStream(
"data.txt",
{
highWaterMark:
1024
}
);
↓
1 KB/chunk
Nếu không cấu hình, Node.js sẽ sử dụng giá trị mặc định tùy theo loại Stream.
Ví dụ thực tế
Đếm số byte đã đọc.
let total = 0;
stream.on(
"data",
(chunk)=>{
total +=
chunk.length;
});
stream.on(
"end",
()=>{
console.log(
total
);
});
↓
102400
Stream trong Node.js
Bạn sẽ gặp Stream ở rất nhiều nơi.
HTTP
Express
Upload
Download
Video
Audio
ZIP
Cloud Storage
Database Export
Buffer và Stream khác nhau thế nào?
| Buffer | Stream |
|---|---|
| Lưu dữ liệu trong RAM | Đọc/Ghi từng phần |
| Phù hợp dữ liệu nhỏ | Phù hợp dữ liệu lớn |
| Truy cập toàn bộ dữ liệu ngay | Xử lý theo từng chunk |
| Có thể tốn nhiều RAM | Tiết kiệm bộ nhớ |
Khi nào dùng Buffer?
Buffer phù hợp khi:
Chuỗi nhỏ.
Mã hóa dữ liệu.
Xử lý Binary.
Chuyển đổi Encoding.
Làm việc với các gói dữ liệu nhỏ.
Khi nào dùng Stream?
Nên dùng:
Upload ảnh.
Upload video.
Download file.
HTTP Server.
Sao chép file lớn.
Xử lý log.
Truyền dữ liệu thời gian thực.
Những lỗi thường gặp
Dùng readFile() với file quá lớn
await fs.readFile(
"movie.mp4"
);
Có thể tiêu tốn rất nhiều RAM hoặc làm ứng dụng phản hồi chậm.
Hãy cân nhắc sử dụng createReadStream() với các tệp lớn.
Quên xử lý lỗi
stream.on(
"error",
(err)=>{
console.log(err);
});
Nếu không lắng nghe sự kiện error, chương trình có thể dừng khi gặp lỗi đọc hoặc ghi file.
Quên gọi end()
stream.write(
"Hello"
);
Nếu không gọi:
stream.end();
Stream ghi có thể chưa đóng đúng cách và dữ liệu có thể chưa được ghi hoàn tất.
Nhầm Buffer với String
console.log(
chunk
);
↓
<Buffer ...>
Muốn đọc dạng văn bản:
chunk.toString();
hoặc:
stream.setEncoding(
"utf8"
);
Bảng tổng hợp
| API | Ý nghĩa |
|---|---|
Buffer.from() | Tạo Buffer |
Buffer.alloc() | Tạo Buffer rỗng |
Buffer.byteLength() | Đếm số byte |
createReadStream() | Đọc Stream |
createWriteStream() | Ghi Stream |
pipe() | Nối hai Stream |
write() | Ghi dữ liệu |
end() | Kết thúc Stream |
Tổng kết
Trong bài học này, bạn đã tìm hiểu hai khái niệm rất quan trọng của Node.js là Buffer và Stream. Buffer dùng để lưu trữ dữ liệu nhị phân trong bộ nhớ, còn Stream cho phép đọc và ghi dữ liệu theo từng phần nhỏ, giúp tiết kiệm RAM và xử lý hiệu quả các tệp có dung lượng lớn.
Đây là nền tảng cho rất nhiều tính năng trong Node.js như HTTP Server, Upload/Download, xử lý video, âm thanh, lưu trữ đám mây và truyền dữ liệu thời gian thực.
Bài tiếp theo
👉 Bài 11 — Callback
Bạn sẽ học:
Callback là gì.
Callback hoạt động như thế nào.
Callback đồng bộ và bất đồng bộ.
Callback Hell là gì.
Vì sao Promise và Async/Await ra đời để giải quyết những hạn chế của Callback.
x1
quay về MỤC LỤC




Không có nhận xét nào:
Đăng nhận xét