Building Reliable AWS S3 Uploads
Cover photo by Growtika on Unsplash
Saving files directly to your server disk is tempting. It is easy, it works on your machine, and it keeps your logic in one place. I admit, for a side project where you are the only user, it is fine. Sometimes simplicity beats architectural purity.
Why local storage fails
“But why not just save it to the public folder?” This is the classic objection. The issue is persistence and scaling. If you deploy on ephemeral platforms, your files vanish every time the container restarts. Even if you use a persistent volume, you create a coupling between your app logic and the file system. It becomes a mess when you move to a multi-instance setup.
I recommend using AWS S3 for anything beyond a prototype. It abstracts the file system away. You aren’t managing disk space anymore. You are dealing with objects. Here is how I usually set up the presigned URL flow to keep my server out of the actual bytes transmission.
Use presigned URLs
Don’t upload the file from the user browser to your backend, then to S3. That is slow, expensive, and burns server memory. Instead, generate a presigned URL on your server and send it to the client. The client talks directly to S3.
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const s3 = new S3Client({ region: "us-east-1" });
async function getUploadUrl(key) { const command = new PutObjectCommand({ Bucket: "my-bucket", Key: key }); return await getSignedUrl(s3, command, { expiresIn: 60 });}Security matters
“Won’t people upload malicious files?” Yes, they will. You cannot trust the client. Even if you sign the URL, you need to enforce content types and file sizes on the bucket policy side. Use the Conditions field in your S3 policy when generating the URL. It limits the user to specific MIME types.
Performance considerations
I should mention that S3 latency is sometimes unpredictable (sometimes it’s fast, sometimes it hits a snag). If your app is image-heavy, you absolutely need a Content Delivery Network like CloudFront in front of that bucket. Don’t serve files directly from the bucket in a production environment if you care about speed. It’s just bad practice.
Also, handling concurrent uploads can get tricky if you aren’t careful about unique keys. I usually append a UUID to every file name. Collisions are painful to debug later (I’ve been there, it’s not fun).
Keep it simple
Is there a case where S3 is overkill? Maybe. If you have a massive amount of small files, the metadata overhead can get weird. But for most web applications, it remains the standard for a reason. It is reliable. It just works.
What are your specific hurdles when setting up your first bucket? I’m curious if you find the SDK overhead to be too much or if you’ve found a better abstraction layer for this stuff.