Upload file tưởng đơn giản nhưng thường là nguồn lỗi và rủi ro bảo mật trong ứng dụng web. Nếu để client gửi file qua server trung gian, bạn dễ gặp tình trạng tốn băng thông, nghẽn server, timeout và khó mở rộng. Một hướng thực tế là cho client upload trực tiếp lên dịch vụ lưu trữ bằng pre-signed URL, còn server chỉ xác thực và cấp quyền. Kiến trúc đề xuất gồm bốn bước: - Client gửi metadata: tên file, loại nội dung, kích thước dự kiến. - Server kiểm tra quyền, giới hạn dung lượng, loại file được phép, rồi tạo pre-signed URL có thời hạn ngắn. - Client upload trực tiếp lên storage, hiển thị tiến trình và hỗ trợ hủy hoặc thử lại khi mạng gián đoạn. - Server nhận thông báo hoàn tất, xác thực lại file, lưu metadata và kích hoạt quét virus bất đồng bộ nếu cần. Ví dụ thực tế với Node.js và một dịch vụ tương thích S3: API POST /uploads/init nhận metadata, kiểm tra user đã đăng nhập và file có đuôi hợp lệ. Server trả về URL upload cùng các header bắt buộc. Client dùng fetch hoặc XMLHttpRequest để upload, lắng nghe sự kiện progress, dùng AbortController để hủy khi người dùng chọn file khác. Với file lớn, có thể chia thành nhiều phần và upload song song có kiểm soát. Sau khi upload xong, client gọi POST /uploads/complete; server kiểm tra object tồn tại, so khớp kích thước và content-type, rồi ghi bản ghi vào cơ sở dữ liệu. Một số lời khuyên có thể áp dụng ngay: - Không tin dữ liệu từ client. Luôn giới hạn kích thước, loại file và thời hạn URL ở phía server. - Dùng tên file ngẫu nhiên hoặc tiền tố theo user để tránh ghi đè và đoán đường dẫn. - Cấu hình CORS chặt chẽ cho bucket, chỉ cho phép origin cần thiết. - Hiển thị tiến trình, cho phép hủy và thử lại; đặt timeout hợp lý cho từng phần upload. - Ghi log các lần upload thất bại và dọn file rác định kỳ. - Kiểm thử các tình huống mất mạng, upload lại, file quá lớn và content-type giả mạo. Cách làm này giữ server nhẹ, tăng tốc độ upload và vẫn kiểm soát được bảo mật nếu bạn xác thực đúng ở cả hai phía.