Họ đang đo gì
Bạn có nghĩ tới băng thông và bộ nhớ của server không, và có biết mẫu presigned URL không.
Trả lời ngắn~30 giây
Không cho file đi qua server. Client xin một presigned URL (server kiểm tra quyền, kích thước tối đa và kiểu file rồi ký), upload thẳng lên S3/R2/Supabase Storage, sau đó báo lại cho server để ghi metadata. Server không bao giờ giữ file trong bộ nhớ, không tốn băng thông, và việc mở rộng thành vấn đề của nhà cung cấp lưu trữ chứ không phải của bạn.
Giải thích sâu
Lý do quan trọng nhất không phải băng thông mà là bộ nhớ và thời gian giữ kết nối. Một upload 50MB trên mạng chậm chiếm một luồng hoặc một kết nối trong nhiều phút; vài chục upload cùng lúc là đủ để một instance ngừng phục vụ request thường. Presigned URL cắt hẳn vấn đề đó, và đổi lại bạn phải chuyển việc kiểm tra sang lúc ký, kèm với giới hạn content-length-range trong policy.
Phần bảo mật hay bị sơ suất là tin vào Content-Type mà client khai. Ai cũng đặt được header đó, nên phải xác minh nội dung THẬT sau khi upload — đọc magic bytes, và với ảnh thì tốt nhất là xử lý lại qua một thư viện (resize, chuyển định dạng) vì việc đó vừa chuẩn hoá vừa loại bỏ payload nhúng trong metadata. Và luôn phục vụ file từ một domain khác domain ứng dụng, để một file HTML lọt lưới không chạy được trong ngữ cảnh phiên của người dùng.
Cuối cùng là file mồ côi: client xin URL, upload xong rồi thoát trước khi gọi bước xác nhận. Bạn có file trong storage mà không có hàng nào trong database. Cách xử lý là upload vào tiền tố tạm rồi chuyển sang tiền tố chính khi xác nhận, cộng với vòng đời tự xoá cho tiền tố tạm sau 24 giờ — cấu hình một lần ở bucket và không phải viết job dọn dẹp nào.
Câu hỏi tiếp theo họ sẽ hỏi
?File rất lớn thì sao?
Multipart upload: chia thành nhiều phần, mỗi phần một presigned URL, client upload song song và thử lại từng phần khi lỗi. Điều này còn cho phép tiếp tục sau khi mất mạng, thay vì làm lại từ đầu.
Trả lời thế này là mất điểm
- Đọc cả file vào bộ nhớ rồi mới ghi. Với file lớn hoặc nhiều người upload cùng lúc thì đó là OOM.
- Lưu file trong ổ đĩa của container. Container chết là file mất, và nhiều instance thì mỗi cái giữ một phần khác nhau.