Big files

You need: storage composed.

Why multipart at all

Letting users upload is the single-part version, the object model is why every part lands under one key, quotas and cleanup is what an abandoned multipart costs, and the reference has the sizes.

A single presigned PUT is capped at 5 GiB, and it is not resumable. A dropped connection at 4 GiB starts over.

Multipart raises the ceiling and makes a lost client cheap.

The defaults

Threshold100 MiB — above this an upload goes multipart
Part size64 MiB
Minimum part5 MiB, every part but the last
Maximum part5 GiB
Parts per upload10,000 — R2’s hard cap

Below the threshold, one presigned PUT is simpler and cheaper.

The ceiling those imply

10,000 × 64 MiB ≈ 625 GiB per object.

That is well short of R2’s own object cap. Storing objects larger than that means raising partSizeBytes, which gets you a proportionally higher ceiling.

The 5 MiB floor is S3’s rule, not the kit’s

R2 rejects a completion whose non-final part is smaller.

Enforcing it at plan time means the failure lands with a config-shaped message, rather than after the client has already spent bandwidth on every part.

Resuming

GET /storage/<id>/parts

Re-lists what R2 actually holds and re-presigns the rest. The client sends only what is missing.

The server holds no transfer state beyond the pending row — which is what makes resume free rather than a feature with its own bookkeeping.

POST /storage/<id>/complete
POST /storage/<id>/abort

Quota still applies

The pending row reserves the declared size the moment it is written, so a multipart upload of 300 GiB is reserved against the owner’s quota from the first part, not the last.

An abandoned multipart upload past its TTL is reclaimed by the sweep — and emptying a bucket on teardown deletes the parts of an incomplete upload too, because R2 refuses to delete a bucket that still holds them.

Serving a big file back

GET /storage/<id>

Range requests are supported. The download response sets the headers that stop a browser executing what it got, whatever the stored content type says.

For a file that should be reachable by link rather than by session, see sharing files.

When it is a video

A large video usually wants media rather than raw storage — Cloudflare Stream ingests, transcodes and serves adaptive HLS, and the enrichment path reads its audio rendition to transcribe it.

Storage is the right layer when you need the bytes back exactly as they went in.

ESC