AWS S3 — File Storage
Your Files in the Cloud
Open interactive version (quiz + challenge)Real-world analogy
S3 is like a magical storage locker with unlimited space. You give each file a unique key (like a locker number), and S3 stores it safely in the cloud. Need it back? Just ask for it by key. It never runs out of space and it never loses your files!
What is it?
Amazon S3 (Simple Storage Service) is cloud-based object storage. It's where you store files like images, documents, videos, and backups. Files are organized in buckets and accessed via unique keys.
Real-world relevance
Netflix stores all their content on S3. Airbnb stores listing photos. Almost every app that handles file uploads uses S3 or a similar service (Google Cloud Storage, Azure Blob).
Key points
- Object Storage — S3 stores files as objects in containers called buckets. Each object has a unique key, the file data, and metadata. S3 is flat — no real folders, just key prefixes like uploads/images/photo.jpg that mimic directories.
- Pre-Signed URLs — Generate a temporary URL granting upload or download access for a limited time without exposing AWS credentials. The frontend uploads directly to S3, bypassing your server. Reduces load and speeds up file uploads.
- Scalable & Durable — S3 offers 99.999999999% durability (11 nines) — store 10 million files and expect to lose one every 10,000 years. Data replicates across facilities. Store 1 or 1 billion files with no capacity planning.
- CDN Integration — Pair S3 with CloudFront CDN to serve files from edge servers nearest to users worldwide. Someone in Tokyo gets images from a nearby server, not US-East. Reduces latency to milliseconds for media-heavy apps.
- Versioning — Enable bucket versioning to keep every version of every file. Accidentally deleted an image or overwrote a document? Restore a previous version with one API call. Also protects against malicious deletions.
- Access Control — Control access with bucket policies, IAM roles, and ACLs. Make profile images public while keeping invoices private. Grant your backend upload permissions via IAM without access to delete other buckets.
- Cost Effective — S3 pricing is pay-as-you-go: about $0.023 per GB/month for standard storage. Store thousands of images for pennies. Use S3 Glacier for rarely-accessed files at even lower rates. Pay only for what you use.
- Storage Classes — S3 offers storage classes: Standard for frequent access, Intelligent-Tiering for unpredictable patterns, Glacier for archival. Set lifecycle rules to auto-move files to cheaper tiers after 30 or 90 days.
- File Upload Best Practices — Generate unique keys with timestamps or UUIDs to prevent collisions: uploads/1709234567-photo.jpg. Validate file types and sizes on both frontend and backend. Set Content-Type so browsers render files correctly.
- Multipart Uploads — For files over 100MB, multipart upload splits them into chunks uploaded in parallel. If one chunk fails, only that chunk retries. AWS SDK handles this via the Upload class, making large uploads reliable.
Code example
// 1. Upload a file to S3 📤
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
const s3 = new S3Client({ region: 'us-east-1' });
async function uploadFile(file: Buffer, fileName: string) {
await s3.send(new PutObjectCommand({
Bucket: 'my-app-uploads',
Key: `uploads/${Date.now()}-${fileName}`,
Body: file,
ContentType: 'image/jpeg',
}));
}
// 2. Generate a pre-signed URL (temporary access) 🔗
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
async function getUploadUrl(fileName: string) {
const command = new PutObjectCommand({
Bucket: 'my-app-uploads',
Key: `uploads/${fileName}`,
});
// URL valid for 15 minutes — then it expires!
const url = await getSignedUrl(s3, command, { expiresIn: 900 });
return url; // Frontend uses this URL to upload directly!
}
// 3. NestJS file upload endpoint 📁
@Controller('files')
export class FileController {
@Post('upload')
@UseInterceptors(FileInterceptor('file'))
async upload(@UploadedFile() file: Express.Multer.File) {
const key = `uploads/${Date.now()}-${file.originalname}`;
await this.s3Service.upload({
bucket: 'my-app-uploads',
key,
body: file.buffer,
contentType: file.mimetype,
});
return { url: `https://my-app-uploads.s3.amazonaws.com/${key}` };
}
}Line-by-line walkthrough
- 1. 1. Upload a file to S3 📤
- 2. Importing required dependencies
- 3.
- 4. Declaring a variable
- 5.
- 6. Declaring a function
- 7. Waiting for an async operation to complete
- 8.
- 9.
- 10.
- 11.
- 12.
- 13. Closing block
- 14.
- 15. 2. Generate a pre-signed URL (temporary access) 🔗
- 16. Importing required dependencies
- 17.
- 18. Declaring a function
- 19. Declaring a variable
- 20.
- 21.
- 22.
- 23.
- 24. URL valid for 15 minutes — then it expires!
- 25. Declaring a variable
- 26. Returning a value
- 27. Closing block
- 28.
- 29. 3. NestJS file upload endpoint 📁
- 30. Decorator that adds metadata or behavior
- 31. Exporting for use in other files
- 32. Decorator that adds metadata or behavior
- 33. Decorator that adds metadata or behavior
- 34.
- 35. Declaring a variable
- 36.
- 37. Waiting for an async operation to complete
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44. Returning a value
- 45. Closing block
- 46. Closing block
Spot the bug
@Post('upload')
async upload(@UploadedFile() file: Express.Multer.File) {
const key = file.originalname;
await this.s3.upload({ Bucket: 'my-bucket', Key: key, Body: file.buffer });
return { url: 'https://my-bucket.s3.amazonaws.com/' + key };
}Need a hint?
What happens when two users upload files with the same name?
Show answer
Using file.originalname as the S3 key means files with the same name overwrite each other. Fix: add a unique prefix: const key = Date.now() + '-' + file.originalname;
Explain like I'm 5
S3 is like a magical closet in the sky with infinite space. You can put anything in it - photos, videos, homework. Each thing gets a special label so you can find it later. The closet never runs out of room and never loses your stuff!
Fun fact
Amazon S3 stores over 100 TRILLION objects (files). If each object was a grain of sand, you could fill about 1,000 Olympic swimming pools! 🏊
Hands-on challenge
Create a free AWS account, make an S3 bucket, and upload a file using the AWS console. Then try generating a pre-signed URL using the AWS CLI!
More resources
- AWS S3 Documentation (AWS Official)
- NestJS File Upload (NestJS Official)
- AWS S3 in 100 Seconds (Fireship)