1. 00:45 06/08/2026
Upload file nghe có vẻ là tính năng đơn giản: chọn file, bấm gửi, lưu lại. Nhưng trong thực tế, đây là một trong những khu vực dễ sinh lỗi nhất của web/app: file quá lớn làm nghẽn server, người dùng mạng yếu bị mất tiến trình, ảnh độc hại đội lốt file hợp lệ, hoặc thư mục lưu trữ phình to mà không ai dọn.
Dưới đây là một số kinh nghiệm thực tế giúp thiết kế tính năng upload ổn định và dễ bảo trì hơn.
1. Đừng chỉ kiểm tra loại file bằng đuôi mở rộng
Nhiều hệ thống chỉ kiểm tra tên file có kết thúc bằng .jpg, .png, .pdf hay không. Cách này không đủ an toàn, vì người dùng có thể đổi tên file tùy ý.
Nên kiểm tra ít nhất các lớp sau:
- Đuôi file: dùng để chặn nhanh các định dạng không mong muốn.
- MIME type: kiểm tra loại nội dung do client hoặc thư viện đọc được.
- Chữ ký file, còn gọi là magic bytes: kiểm tra vài byte đầu để xác nhận file có đúng định dạng không.
- Với ảnh, nên thử đọc và xử lý lại bằng thư viện ảnh đáng tin cậy trước khi lưu bản dùng cho hiển thị.
Ví dụ: nếu hệ thống chỉ cho upload ảnh đại diện, bạn không nên lưu nguyên file người dùng gửi lên rồi hiển thị trực tiếp. Hãy đọc ảnh, resize/crop theo kích thước chuẩn, xuất lại thành JPEG/WebP/PNG, rồi lưu bản đã xử lý.
2. Luôn giới hạn dung lượng và kích thước
Một lỗi phổ biến là chỉ giới hạn dung lượng ở giao diện. Người dùng bình thường sẽ thấy thông báo, nhưng request gửi trực tiếp bằng công cụ khác vẫn có thể vượt qua.
Nên đặt giới hạn ở nhiều tầng:
- Frontend: cảnh báo sớm để người dùng không chờ vô ích.
- Backend: từ chối request vượt quá giới hạn.
- Web server hoặc reverse proxy: cấu hình giới hạn kích thước body.
- Storage policy: tránh một tài khoản upload vô hạn.
Ví dụ thực tế: ảnh đại diện thường không cần lớn hơn vài MB, vì cuối cùng cũng sẽ được resize về kích thước nhỏ. Tài liệu PDF có thể cần giới hạn khác. Video lại nên có luồng xử lý riêng, không nên gom chung với upload ảnh.
3. Tách upload khỏi thư mục chạy ứng dụng
Không nên lưu file upload vào cùng thư mục public của mã nguồn rồi cho truy cập trực tiếp một cách tùy tiện. Nếu bắt buộc public, hãy kiểm soát rõ đường dẫn và loại file được phục vụ.
Cách làm an toàn hơn:
- Lưu file vào object storage hoặc thư mục riêng ngoài mã nguồn.
- Đổi tên file thành ID ngẫu nhiên hoặc hash, không dùng trực tiếp tên gốc.
- Lưu tên gốc trong database nếu cần hiển thị cho người dùng.
- Không cho phép thực thi file trong thư mục upload.
Ví dụ: thay vì lưu invoice_thang_8.pdf vào /public/uploads/invoice_thang_8.pdf, hãy lưu thành một key như uploads/2026/08/uuid.pdf, còn tên gốc được lưu ở cột original_filename.
4. Dùng upload trực tiếp lên storage khi file lớn
Với file nhỏ, client gửi lên backend rồi backend lưu tiếp sang storage vẫn ổn. Nhưng với file lớn, cách này làm backend tốn băng thông và dễ nghẽn.
Một mô hình phổ biến hơn:
- Client gọi API xin quyền upload.
- Backend tạo URL upload tạm thời hoặc token giới hạn.
- Client upload trực tiếp lên storage.
- Backend nhận callback hoặc client báo hoàn tất.
- Backend kiểm tra lại metadata rồi ghi nhận file hợp lệ.
Cách này giúp backend nhẹ hơn, đặc biệt khi upload ảnh số lượng lớn, tài liệu nặng hoặc video.
5. Thiết kế trạng thái file rõ ràng
Đừng coi file vừa upload xong là đã dùng được ngay trong mọi trường hợp. Nhiều file cần qua bước kiểm tra virus, resize ảnh, tạo thumbnail, trích xuất metadata hoặc chuyển mã.
Có thể dùng các trạng thái như:
- pending: đã nhận upload, đang chờ xử lý.
- processing: đang xử lý.
- ready: có thể sử dụng.
- failed: xử lý lỗi, cần báo người dùng hoặc cho upload lại.
Ví dụ: khi người dùng upload ảnh sản phẩm, giao diện có thể hiển thị “Đang xử lý ảnh”. Sau khi queue tạo thumbnail xong, sản phẩm mới dùng ảnh đó làm ảnh chính.
6. Đừng quên dọn rác
Upload thường sinh ra nhiều file mồ côi: người dùng chọn file rồi bỏ form, upload thất bại giữa chừng, bản cũ không còn được tham chiếu, file tạm sau xử lý không bị xóa.
Nên có cơ chế định kỳ:
- Xóa file pending quá lâu.
- Xóa file không còn liên kết với bản ghi nào.
- Xóa file tạm sau khi xử lý xong.
- Ghi log khi xóa để dễ kiểm tra nếu có khiếu nại.
7. Trải nghiệm người dùng cũng quan trọng
Một upload tốt không chỉ là an toàn, mà còn phải dễ dùng:
- Hiển thị tiến trình upload nếu file lớn.
- Cho phép hủy upload.
- Báo lỗi cụ thể: sai định dạng, quá dung lượng, mạng lỗi, xử lý thất bại.
- Nếu có thể, hỗ trợ retry thay vì bắt người dùng chọn lại từ đầu.
Kết luận
Upload file là nơi giao nhau giữa backend, frontend, storage, bảo mật và trải nghiệm người dùng. Nếu làm vội, tính năng này rất dễ trở thành nguồn lỗi âm thầm. Một thiết kế tốt nên có kiểm tra nhiều lớp, giới hạn rõ ràng, lưu trữ tách biệt, trạng thái xử lý minh bạch và cơ chế dọn dẹp định kỳ.
Lời khuyên thực tế: trước khi viết code upload, hãy trả lời vài câu hỏi đơn giản: ai được upload, upload loại gì, dung lượng tối đa bao nhiêu, file được lưu ở đâu, khi nào được xem là hợp lệ, và khi không còn dùng nữa thì ai xóa. Trả lời được các câu này, phần triển khai sẽ ít rối hơn rất nhiều.
0