File Upload with Multer, Express.js
Vấn đề khi upload file với Body Parser
Khi bắt đầu xây dựng tính năng upload ảnh, bạn có thể nghĩ đơn giản là thay thế imageURL (string) bằng một input file picker để nhận file từ người dùng:
Tuy nhiên, khi thử console.log(req.body.image), bạn sẽ gặp vấn đề: không thể in ra được nội dung file. Tại sao?
Nguyên nhân
Express mặc định sử dụng middleware body-parser với cấu hình:
Middleware urlencoded chỉ xử lý dữ liệu dạng text và chuyển tất cả form data thành string. Trong khi đó, file ảnh là dữ liệu binary - một định dạng hoàn toàn khác mà body-parser không thể xử lý.
Giải pháp: Multer
Để upload file, bạn cần:
Thay đổi encoding type của form thành multipart/form-data:
Cài đặt và cấu hình Multer:
Cách Multer hoạt động
Một điểm quan trọng cần hiểu: Multer chạy song song với các middleware khác của Express. Multer chỉ chịu trách nhiệm xử lý file bên trong form, còn các input text thông thường vẫn được parse bình thường bởi body-parser.
Khi request đến:
Ví dụ trong form trên:
Cấu hình Multer chi tiết
1. Thiết lập Multer middleware
Sau khi cài đặt Multer, bạn cần thêm middleware vào ứng dụng Express, đặt ngay sau body-parser:
Các phương thức upload của Multer:
2. Cấu trúc dữ liệu file trong req.file
Khi console.log(req.file), bạn sẽ thấy object chứa thông tin file:
Đây là cách Multer “đóng gói” file binary thành object JavaScript để bạn có thể làm việc dễ dàng hơn.
3. Cấu hình đơn giản với dest
Cách nhanh nhất để lưu file là dùng tùy chọn dest:
File sẽ được lưu vào thư mục images/ với tên ngẫu nhiên (không có extension). Tuy nhiên, cách này hạn chế vì bạn không kiểm soát được tên file.
4. Cấu hình nâng cao với diskStorage
Để kiểm soát chi tiết hơn về nơi lưu và cách đặt tên file, sử dụng diskStorage:
Giải thích callback pattern:
Cả destination và filename đều nhận 3 tham số:
Tại sao phải dùng callback?
Multer xử lý file bất đồng bộ (asynchronous). Callback cho phép bạn:
Cú pháp callback:
Ví dụ nâng cao hơn:
Lưu ý quan trọng:
Lọc file với fileFilter và thông báo lỗi
Để đảm bảo người dùng chỉ upload đúng loại file mong muốn, Multer cung cấp tùy chọn fileFilter:
Cách hoạt động:
Hiển thị thông báo lỗi khi file không hợp lệ
Cách 1: Throw error trực tiếp trong fileFilter
Cách 2: Xử lý lỗi trong route handler (linh hoạt hơn)
Cách 3: Sử dụng error handling middleware
Các loại filter khác
1. Giới hạn kích thước file
Khi file vượt quá giới hạn, Multer sẽ throw MulterError với code LIMIT_FILE_SIZE.
2. Giới hạn số lượng file
3. Các giới hạn khác
4. Filter tổng hợp (Best Practice)
Lưu ý bảo mật:
Lưu trữ và phục vụ file sau khi upload
1. Lưu thông tin file vào Database
Sau khi upload thành công, bạn nên lưu thông tin file vào database thay vì chỉ lưu trên disk:
Lưu ý:
2. Serve Public Files (Static Files)
Để cho phép truy cập trực tiếp file qua URL, sử dụng Express static middleware:
Người dùng có thể truy cập: http://localhost:3000/images/1234567890-photo.jpg
Best Practice cho Public Files:
3. Serve Private Files (Protected Routes)
Đối với file cần bảo mật (invoice, documents cá nhân), không để public mà tạo route có authentication:
4. Cách trả file về client
Express cung cấp nhiều method để serve file, mỗi method phù hợp với mục đích khác nhau:
a) res.download() - Download file ngay
b) res.sendFile() - Hiển thị file trên trình duyệt
c) Custom headers (kiểm soát chi tiết nhất)
Best Practice cho File Headers:
5. Streaming vs Preloading Data
❌ KHÔNG NÊN: Preloading (fs.readFile)
Vấn đề:
✅ NÊN DÙNG: Streaming (fs.createReadStream)
Ưu điểm Streaming:
Advanced: Streaming với Range Requests (cho video)
Tóm tắt:
Method
Khi nào dùng
Memory
Performance
fs.readFile()
File nhỏ (<1MB), cần xử lý data
Cao
Thấp
fs.createReadStream()
File lớn, download, video
Thấp
Cao
res.download()
Download đơn giản
Thấp
Cao
res.sendFile()
Serve static, images, PDFs
Thấp
Cao
Best Practice cuối cùng:
Tạo PDF động với PDFKit
Thay vì lưu trữ hàng ngàn file PDF invoice trên server, bạn có thể tạo PDF on-the-fly (động) mỗi khi user yêu cầu tải về.
1. Cài đặt PDFKit
2. Tạo và serve PDF trực tiếp
Cách hoạt động:
3. Tạo PDF mà KHÔNG lưu trữ (Pure On-the-fly)
Nếu bạn không muốn lưu file PDF trên server:
Ưu điểm:
Nhược điểm:
4. Xóa file PDF sau khi download (hoặc sau thời gian)
Cách 1: Xóa ngay sau khi response hoàn tất
Cách 2: Xóa sau một khoảng thời gian (ví dụ: 1 giờ)
Cách 3: Scheduled cleanup job (Best Practice cho production)
5. Tạo Invoice PDF chi tiết hơn
Best Practices
1. Chọn strategy phù hợp:
2. Performance:
3. Security:
4. Error handling:
