FileDummy Logo
FileDummy
Lưu Trữ Đám Mây & CDN

Truyền Luồng (Stream) Tải Tệp Lớn Trong Node.js Tránh Lỗi Tràn Bộ Nhớ RAM

Cách sử dụng luồng đọc Node.js Stream và cơ chế backpressure để phân phối tệp tin dung lượng 1GB mà không tốn tài nguyên máy chủ.

20 tháng 9, 202610 phút đọc1,420 lượt xem
nodejsstreamingexpressfile-downloadperformance

Phục vụ tệp lớn trực tiếp từ Node.js mà không có CDN hoặc proxy object storage đòi hỏi phải sử dụng streaming. Tải một tệp 1GB vào bộ nhớ bằng fs.readFileSync sẽ tiêu tốn 1GB RAM máy chủ cho mỗi người dùng kết nối — nhanh chóng dẫn đến crash tiến trình.

Streaming Tệp Với HTTP Streams Của Node.js

Readable stream của Node.js chỉ buffer một lượng nhỏ chunk (thường là 64KB) trong bộ nhớ tại bất kỳ thời điểm nào:

TypeScript
import http from 'http';
import fs from 'fs';
import path from 'path';

const server = http.createServer((req, res) => {
  const filePath = path.join(process.cwd(), 'files', 'large-dataset.pdf');

  fs.stat(filePath, (err, stats) => {
    if (err) {
      res.writeHead(404, { 'Content-Type': 'text/plain' });
      return res.end('Không tìm thấy tệp');
    }

    // Thiết lập header response nhị phân
    res.writeHead(200, {
      'Content-Type': 'application/pdf',
      'Content-Length': stats.size,
      'Content-Disposition': 'attachment; filename="large-dataset.pdf"',
      'Accept-Ranges': 'bytes',
    });

    const readStream = fs.createReadStream(filePath);

    // Tự động xử lý TCP backpressure
    readStream.pipe(res);

    // Xử lý khi client ngắt kết nối
    req.on('close', () => {
      readStream.destroy();
    });
  });
});

server.listen(3000, () => console.log('Máy chủ tải xuống đang chạy trên cổng 3000'));
Thử Nghiệm Thực Tế.pdf

Tải file PDF mẫu 500MB để kiểm thử xử lý stream backpressure trong code máy chủ của bạn.

Tải File Mẫu 500MB PDF →

Hiểu Stream Backpressure

Khi máy chủ có thể đọc từ đĩa nhanh hơn tốc độ client nhận dữ liệu qua mạng, stream.pipe(res) tự động tạm dừng read stream cho đến khi buffer socket drain của client được giải phóng, giúp bảo toàn bộ nhớ máy chủ.

NDL

Nguyễn Đại Long

Tác Giả

Backend Lead • Chuyên gia Kiến trúc Hệ thống Phân tán & Lưu trữ Đám mây

Hơn 4 năm kinh nghiệm thiết kế các hệ thống xử lý tệp tải lên thông lượng lớn, tối ưu hóa cơ sở dữ liệu và hạ tầng phân tán Cloudflare R2 / AWS S3. Người sáng lập FileDummy và Mạng lưới Hệ sinh thái NDL.

Bài viết này có hữu ích không?

Bấm Thích để ủng hộ tác giả và giúp bài viết lan tỏa tới cộng đồng lập trình viên.

Thảo Luận Kỹ Thuật & Đóng Góp Ý Kiến (0)

Chia sẻ kết quả benchmark, phản hồi các trường hợp biên hoặc đặt câu hỏi chuyên môn.

0/3000

Chưa có bình luận nào. Hãy là lập trình viên đầu tiên bắt đầu cuộc thảo luận!

Câu Hỏi Thường Gặp (FAQ)

Why does Node.js run out of memory when serving large files?▾

Using fs.readFileSync() or res.send(buffer) loads the entire file into heap memory. For a 1GB file with 10 concurrent downloads, that is 10GB of RAM. Use fs.createReadStream().pipe(res) instead — it reads in 64KB chunks, keeping memory constant regardless of file size.

What is backpressure in Node.js streams?▾

Backpressure occurs when the writable stream (HTTP response) cannot consume data as fast as the readable stream (file read) produces it. Using .pipe() handles backpressure automatically by pausing the file read when the response write buffer is full.

How do I add HTTP Range request support to my Node.js file server?▾

Parse the Range header, calculate the byte range, use fs.createReadStream(path, { start, end }), and respond with 206 Partial Content and the Content-Range header. This enables browser resume support and video seeking.

Bài Viết Liên Quan