Back to All Projects
2026

AWS S3 Image Upload App

Portfolio project by Nanakumor Princewill

Password-gated image gallery with direct S3 uploads via presigned URLs, server fallback with magic-byte validation, cursor pagination, and signed downloads.

AWS S3 Image Upload App — project screenshot by Nanakumor Princewill

Built for

Anyone who needs a small, secure image gallery where browsing can stay public, but uploads and deletes stay behind a password — and large files skip the Node process when possible.

Problem

Routing every upload through an app server wastes bandwidth and hits timeouts. Public demos also need a simple access gate, brute-force protection, and validation that doesn’t trust browser MIME types alone.

Overview

A Next.js 16 gallery focused on cloud storage patterns: browsers upload to S3 with short-lived presigned URLs, an authenticated multipart fallback sniffs file bytes with file-type, and downloads use signed URLs or an optional CDN. A shared password unlocks mutations with HMAC sessions and a 2-attempt / 5-hour lockout. Cursor pagination keeps browsing efficient without dumping the whole bucket into one response.

Capabilities

  • Password-gated uploads with HMAC-signed HttpOnly session cookies (7-day TTL)
  • Dual-layer brute-force lockout: 2 failed attempts → 5-hour device lock
  • Direct browser → S3 PUT via 5-minute presigned upload URLs
  • Authenticated multipart fallback with magic-byte validation (file-type) when direct upload fails
  • Public gallery listing under uploads/ with cursor pagination (default 12, max 30)
  • 15-minute signed download URLs, or optional CDN URLs via AWS_S3_CDN_URL
  • Authenticated delete with confirm modal; keys restricted to uploads/ prefix
  • Full-size preview lightbox with prev/next over loaded pages

Architecture notes

  • Next.js 16 App Router + React 19; AWS SDK v3 S3Client + s3-request-presigner
  • Auth module: APP_ACCESS_PASSWORD, HMAC tokens, requireAuth on mutating routes
  • Upload pipeline: POST /api/upload (presign) → client PUT; fallback POST /api/upload/fallback
  • Read path: GET /api/files lists uploads/ and mints GetObject URLs (or CDN) without auth
  • Validation: 5MB cap; jpg/png/webp/gif/avif; fallback sniffs bytes before PutObject
  • Object keys: uploads/{timestamp}-{uuid}-{sanitizedName}

Outcomes

  • Demonstrates production-minded S3 access patterns (presign, validate, signed read, fallback)
  • Keeps large binary traffic off the Node process when the direct path succeeds
  • Shows public-read / private-write asymmetry with a lightweight password gate
  • Deployed Netlify demo with public source for review

Tech stack

  • React
  • TypeScript
  • Next.js 16 (App Router)
  • Tailwind CSS 4
  • AWS S3
  • AWS SDK for JavaScript (v3)
  • file-type
  • S3 Presigned URLs
  • Netlify