dkhoa-dev/nestjs-multer-gridfs là repo công khai, một service NestJS nhỏ lưu file vào MongoDB GridFS. Có bốn route: POST /files/upload, GET /files, GET /files/:id và DELETE /files/:id. Bài này soi một chỗ không đều tay trong code: download thì stream, upload thì không.
Download: stream thẳng ra client
downloadFile gọi bucket.openDownloadStream(id), controller thì stream.pipe(res). Chunk nào từ MongoDB về là đẩy ra client luôn. Server không lúc nào giữ cả file.
Upload: buffer cả file trong bộ nhớ
Route upload dùng FileInterceptor('file') và không truyền option nào. README của Multer ghi: bỏ trống object options thì Multer giữ file trong bộ nhớ, không ghi xuống đĩa. Nghĩa là file.buffer đã chứa nguyên file trước khi GridFS nhận được byte nào.
Sau đó uploadFile bọc buffer này thành stream rồi pipe vào bucket.openUploadStream:
const bufferStream = require('stream').Readable.from(file.buffer);
bufferStream.pipe(uploadStream)
GridFS mặc định cắt file thành chunk 255 * 1024 byte (chunkSizeBytes trong driver mongodb, lockfile ghi bản 6.12.0). Chia chunk thì không sao. Cái đáng lo là buffer đã có sẵn từ trước bước đó.
Multer cũng không giới hạn kích thước file nếu không truyền limits. README liệt kê fileSize với giá trị mặc định là Infinity. Repo này không truyền, nên mỗi lần upload một file lớn là process phải giữ trong bộ nhớ ít nhất đúng bằng dung lượng file đó.
Thêm hai chỗ nhỏ
- README ghi MongoDB chạy cổng 27017, nhưng
src/app.module.tslại kết nối tớimongodb://localhost:62553/fs. Hai chỗ không khớp nhau. downloadFilevàdeleteFilebọc hết trongtry/catchrồi némNotFoundException. Id sai định dạng hay lỗi thật cũng đều trả về 404.
Bài học
- Thấy
pipechưa chắc đã là stream thật, phía trước vẫn có thể là một buffer. Phải xem byte lấy từ đâu ra. - Route upload nào dùng Multer cũng nên đặt
limits.fileSize, bản demo cũng vậy. - Muốn bộ nhớ không phình theo cỡ file thì đưa thẳng stream multipart vào GridFS, đừng đi qua
file.buffer. Repo này chưa làm chỗ đó. - So README với code. Ở repo này hai bên lệch nhau về cổng database.