Streams

Tạo bởi Hoàng Vũ, chỉnh sửa cuối lúc 31 tháng 1, 2025

Streams trong Node.js

Trong Node.js, Streams là một cơ chế giúp xử lý dữ liệu lớn một cách hiệu quả mà không cần tải toàn bộ dữ liệu vào bộ nhớ. Streams hoạt động theo cơ chế chunk-by-chunk (từng phần nhỏ), giúp tiết kiệm bộ nhớ và tăng tốc độ xử lý.

1. Tổng quan về Streams

Có 4 loại Streams chính trong Node.js:

  1. Readable Streams – Luồng chỉ đọc (ví dụ: đọc file).
  2. Writable Streams – Luồng chỉ ghi (ví dụ: ghi file).
  3. Duplex Streams – Luồng hỗ trợ cả đọc và ghi (ví dụ: socket).
  4. Transform Streams – Luồng có thể chỉnh sửa dữ liệu trong quá trình truyền (ví dụ: nén dữ liệu).

Streams hoạt động theo hai chế độ:

  • Flowing Mode – Dữ liệu được đọc và xử lý ngay lập tức khi có sẵn.
  • Paused Mode – Dữ liệu chỉ được xử lý khi gọi read() thủ công.

2. Readable Streams – Đọc dữ liệu từ file

fs.createReadStream() giúp đọc file theo từng phần nhỏ, thay vì đọc toàn bộ file vào bộ nhớ.

Ví dụ: Đọc file bằng Readable Stream

const fs = require("fs");

// Tạo Readable Stream để đọc file
const readStream = fs.createReadStream("largeFile.txt", { encoding: "utf8" });

// Lắng nghe sự kiện "data" khi có dữ liệu được đọc
readStream.on("data", (chunk) => {
  console.log("Đọc được một phần dữ liệu:", chunk);
});

// Lắng nghe sự kiện "end" khi đọc xong
readStream.on("end", () => {
  console.log("Đã đọc xong file.");
});

// Lắng nghe sự kiện "error" khi có lỗi
readStream.on("error", (err) => {
  console.error("Lỗi khi đọc file:", err);
});

Giải thích:

  • fs.createReadStream() giúp đọc file mà không cần tải toàn bộ vào bộ nhớ.
  • Sự kiện "data" được kích hoạt khi một phần dữ liệu (chunk) được đọc.
  • Sự kiện "end" báo hiệu quá trình đọc file đã hoàn tất.
  • Sự kiện "error" giúp xử lý lỗi khi đọc file.

3. Writable Streams – Ghi dữ liệu vào file

fs.createWriteStream() giúp ghi dữ liệu vào file theo từng phần nhỏ.

Ví dụ: Ghi file bằng Writable Stream

const writeStream = fs.createWriteStream("output.txt");

// Ghi dữ liệu vào file
writeStream.write("Dòng 1\n");
writeStream.write("Dòng 2\n");

// Kết thúc Stream
writeStream.end();

// Sự kiện "finish" được gọi khi ghi xong
writeStream.on("finish", () => {
  console.log("Ghi file thành công!");
});

// Xử lý lỗi
writeStream.on("error", (err) => {
  console.error("Lỗi khi ghi file:", err);
});

Giải thích:

  • fs.createWriteStream() tạo một Writable Stream để ghi dữ liệu vào file.
  • write() dùng để ghi từng phần dữ liệu.
  • end() kết thúc quá trình ghi.
  • Sự kiện "finish" báo hiệu file đã được ghi thành công.
  • Sự kiện "error" giúp xử lý lỗi.

4. Xử lý file lớn với Streams

Nếu dùng fs.readFile(), toàn bộ file sẽ được tải vào bộ nhớ, gây lỗi khi file quá lớn. Streams giúp đọc từng phần file mà không tốn quá nhiều RAM.

Ví dụ: Copy file lớn bằng Streams

const readStream = fs.createReadStream("largeFile.txt");
const writeStream = fs.createWriteStream("copy.txt");

// Dùng pipe để truyền dữ liệu từ Readable Stream sang Writable Stream
readStream.pipe(writeStream);

writeStream.on("finish", () => {
  console.log("Copy file thành công!");
});

Giải thích:

  • pipe() giúp truyền dữ liệu từ Readable Stream sang Writable Stream.
  • Không cần dùng .on("data"), vì pipe() tự động xử lý dữ liệu.

5. Transform Streams – Xử lý dữ liệu khi truyền

Transform Stream giúp thay đổi dữ liệu khi nó đi qua stream. Ví dụ: chuyển đổi chữ thường thành chữ hoa khi đọc file.

Ví dụ: Chuyển đổi chữ thường thành chữ hoa với Transform Stream

const { Transform } = require("stream");

// Tạo Transform Stream để chuyển chữ thường thành chữ hoa
const upperCaseTransform = new Transform({
  transform(chunk, encoding, callback) {
    this.push(chunk.toString().toUpperCase());
    callback();
  },
});

// Đọc file, chuyển đổi, rồi ghi vào file mới
fs.createReadStream("input.txt")
  .pipe(upperCaseTransform)
  .pipe(fs.createWriteStream("output.txt"))
  .on("finish", () => console.log("Chuyển đổi thành công!"));

Giải thích:

  • Transform stream dùng để thay đổi nội dung khi đọc dữ liệu.
  • this.push(chunk.toString().toUpperCase()) giúp chuyển đổi dữ liệu sang chữ hoa.
  • pipe() giúp truyền dữ liệu từ Readable Stream → Transform Stream → Writable Stream.

6. Tổng kết

Loại Stream Chức năng Ví dụ sử dụng
Readable Stream Đọc dữ liệu theo từng phần nhỏ fs.createReadStream("file.txt")
Writable Stream Ghi dữ liệu theo từng phần nhỏ fs.createWriteStream("file.txt")
Duplex Stream Hỗ trợ cả đọc và ghi net.Socket (kết nối mạng)
Transform Stream Biến đổi dữ liệu khi truyền qua Chuyển chữ thường thành chữ hoa

Khi làm việc với file lớn, Streams là một lựa chọn tối ưu để tiết kiệm bộ nhớ và cải thiện hiệu suất.

Website Logo

Với hơn 10 năm kinh nghiệm lập trình web và từng làm việc với nhiều framework, ngôn ngữ như PHP, JavaScript, React, jQuery, CSS, HTML, CakePHP, Laravel..., tôi hy vọng những kiến thức được chia sẻ tại đây sẽ hữu ích và thiết thực cho các bạn.

Bình luận

Website Logo

Chào, tôi là Vũ. Đây là blog hướng dẫn lập trình của tôi.

Liên hệ công việc qua email dưới đây.

lhvuctu@gmail.com

Chúng Tôi Trên

Bạn đang muốn học về lập trình website?

Bạn cần nâng cao kiến thức chuyên nghiệp hơn để nâng cao cơ hội nghề nghiệp? Liên hệ