Skip to content
Brijesh.

Case study

Stickers & Banners

Uploading a 1GB print file over a connection that will drop

Sector
ecommerce
Year
2025 — 2026
Role
Frontend engineer
single-file uploadsprint-resolution artwork
1GB+
on connection failureper-segment automatic retry
resumable
  • TypeScript
  • Shopify Liquid
  • React islands
  • AWS S3 pre-signed URLs
  • AWS Lambda
  • IndexedDB
  • Shopify Cart Transform

01

The problem

A browser file upload is a single HTTP request. For a 1GB print file on an ordinary connection that request runs for many minutes, and the probability that nothing interrupts it over that window is not high. When it fails, it fails completely — no partial credit, no resume, and the customer starts again from zero having already waited.

Routing that through the application server is also the wrong shape: it means paying for the bytes twice and holding a long-lived request open for the duration.

02

Approach

The file is segmented client-side and each segment is sent directly to object storage against a short-lived pre-signed URL, so the bytes never touch the application server. Each segment succeeds or fails independently, which means a failure costs one segment rather than the whole file, and retries are automatic and invisible.

Server-side processing — validation, dimension and resolution checks, preview generation — runs on upload completion rather than inline, so the customer is not waiting on it and the work scales with orders instead of with request handlers.

  • Segmented transfer with per-segment automatic retry and exponential backoff
  • Direct-to-storage via short-lived pre-signed URLs — no bytes through the app server
  • Real byte-level progress, not an indeterminate spinner: on a multi-minute upload the difference between 'working' and 'stuck' is the only thing the customer cares about
  • Serverless post-processing triggered on completion for validation and preview generation
  • Upload state cached in IndexedDB so an accidental navigation does not discard the session

03

The rest of the build

Custom print is configurator-heavy in a way standard commerce is not — dimensions are continuous rather than variant-based, and price is a function of size, material and finish rather than a lookup. Pricing is computed live as the customer adjusts, and the resulting line items are assembled through a Shopify cart transform so the cart reflects the true configured product rather than a placeholder.

04

What I would change

Progress reporting should have been built before retry logic, not after. For the first iteration the retries worked correctly and silently, which meant a customer on a bad connection saw a progress bar that appeared to stall — and reloaded the page, discarding work that was actually succeeding. Correct behaviour that the user cannot observe reads as broken behaviour.