Design File Storage

By the Syncfusion DevTutorials team · Last updated

Store QuickBite's menu photos and receipts: object storage for bytes, a metadata database, presigned uploads, CDN downloads and background thumbnails.

Every QuickBite restaurant has menu photos. Every order creates a receipt PDF. Riders upload a photo after delivering an order. Together, this creates millions of files. During dinner time, customers download about 20,000 photos every second.

Files are different from normal database records. A photo may be 5 MB, while a database row may only be a few hundred bytes. People do not search inside image files. They only upload, download or delete them.

The main idea: store file bytes in object storage, store file information in a metadata database, upload directly to storage and serve downloads through a CDN.

What You Will Learn

In this case study, you will:

  1. Write the requirements for file storage.
  2. Estimate storage and bandwidth.
  3. Separate file content from file metadata.
  4. Upload files using presigned URLs.
  5. Serve files through a CDN.
  6. Generate thumbnails in the background.
  7. Build and test a scalable file storage system.

This builds on Lesson 3 (Latency & Distance), Lesson 5 (Caching), Lesson 6 (Databases) and Lesson 9 (Queues & Workers).

Step 1: Requirements

What It Must Do (Functional)

  • Upload photos and PDFs up to 10 MB.
  • Download files using a link.
  • Support full-size and thumbnail images.
  • Delete files when no longer needed.
  • Keep receipts private.
  • Store information about each file.

How Well It Must Do It (Non-Functional)

  • Downloads should start within 100 ms.
  • Files should survive machine or data center failures.
  • Storage should remain affordable.
  • Support many downloads at the same time.
  • Uploads should stay fast during peak traffic.

Step 2: Estimate

QuickBite's storage numbers:

WhatPer daySize eachTotal per day
Photos (menus, delivery proof, profiles)150,0005 MB + 0.5 MB thumbnails825 GB
Receipt PDFs1,000,00060 KB60 GB
All new files885 GB a day, about 26.6 TB a month

What to notice:

  • Storage grows by tens of terabytes every month.
  • A normal database is not a good place for this.
  • One server is not enough.
  • We need distributed file storage.

Downloads at dinner time: 20,000 photo views a second × 300 KB each (phones get a resized photo, not the 5 MB original) × 8 bits = 48 Gbit/s of outgoing bandwidth.

What to notice:

  • 48 Gbit/s is very large.
  • Application servers cannot handle all downloads.
  • We need a CDN.

Step 3: Bytes and Details Live Apart

Split every file into two parts:

  • File bytes go to object storage.
  • File details go to the metadata database.

Object storage stores:

  • Menu photos
  • Receipt PDFs
  • Delivery photos
  • Profile pictures

The metadata database stores:

  • File ID
  • Storage key
  • Owner
  • File type
  • Upload time
  • Thumbnail information

One menu photo: a 140-byte metadata row in the database, and 5.2 MB of bytes in object storage, about 37,449 times bigger.

Do not store large files inside a database. Backups become larger, replication becomes slower and costs increase.

Step 4: Upload Straight to Storage

A simple design looks like this:

  1. Phone uploads file to app server.
  2. App server receives file.
  3. App server uploads file to storage.

This works at first but does not scale well.

A better solution is a presigned URL.

  1. Phone requests an upload URL.
  2. App server checks permissions.
  3. App server generates a presigned URL.
  4. Phone uploads directly to storage.
  5. Metadata is saved after upload.

The URL works for one file, expires after a short time (here 5 minutes), and lets the phone upload straight to storage. Step through one upload:

Here is the same idea in code:

A presigned link only works for a short time

const issuedAt = 1_000;      // seconds
const validFor = 300;        // 5 minutes

function linkStillValid(now) {
  return now <= issuedAt + validFor;
}

for (const now of [1_060, 1_299, 1_301]) {
  console.log(`at ${now} s: ${linkStillValid(now) ? "upload allowed" : "link expired"}`);
}

Expected output:

at 1060 s: upload allowed
at 1299 s: upload allowed
at 1301 s: link expired

What to notice:

  • App servers do not handle large file uploads.
  • Uploads become faster.
  • Storage handles the file transfer.

After upload:

  1. Save metadata.
  2. Add a thumbnail job to a queue.
  3. Workers generate thumbnails.

Step 5: Download From a CDN

A CDN stores copies of files close to users.

A customer in Bengaluru downloads from a nearby server instead of a distant storage region.

Menu photos are ideal for a CDN because:

  • Many users request the same images.
  • Images rarely change.
  • Cache hit rates are high.

The CDN's hit ratio decides how much traffic still reaches object storage. Step through three hit ratios:

Private files can still use a CDN through signed URLs.

If a photo changes, upload it with a new filename instead of replacing the old file.

Step 6: Thumbnails in the Background

Customers do not need the full-size image all the time.

Instead, create:

  • Small thumbnail
  • Medium thumbnail
  • Original image

When an upload finishes, a job goes on a queue and workers make the thumbnails:

Generate thumbnails in background workers. Do not generate them during the upload request.

Step 7: Build It on the Canvas and Test It

The starting design contains:

  • Load balancer
  • 4 photo API servers
  • Object storage
  • Metadata database

There is no CDN.

CheckPasses when
Peak loadp99 ≤ 100 ms and thumbnails ready within 2 seconds
Machine failureErrors under 1% and p99 under 250 ms
CostUnder $1,500/month
RequirementsQueue-based thumbnail generation

Serve QuickBite's menu photos at dinner time

Run the starting design first.

Then:

  • Add a CDN.
  • Switch uploads to presigned URLs.
  • Add a queue.
  • Add thumbnail workers.

What you will probably find:

  • Without a CDN, downloads overload the system.
  • Uploading through app servers creates bottlenecks.
  • Presigned URLs fix upload bottlenecks.
  • One thumbnail worker is a single point of failure.

A design that passes:

  • 2 load balancers
  • 2 photo API servers
  • Object storage
  • CDN
  • 2 database copies
  • Queue
  • 2 thumbnail workers

Common Mistakes

MistakeWrongCorrect
Files in databaseStore images as BLOBsStore bytes in object storage
Proxy uploadsUpload through app serversUpload directly to storage
No CDNServe all downloads from storageUse a CDN
Overwriting filesReuse the same filenameUse new filenames
Inline thumbnailsResize during uploadUse queues and workers

Practice

What to Remember

  • Store file bytes in object storage.
  • Store metadata in a database.
  • Use presigned URLs for uploads.
  • Use a CDN for downloads.
  • Generate thumbnails with queues and workers.
  • Give updated files new names.
  • Bandwidth is often the biggest scaling challenge.

Further Reading

These are the sources this lesson was checked against:

Next: Search autocomplete, where QuickBite suggests dishes and restaurants as you type.