download
Read an object - a Blob-backed StoredFile by default, or a ReadableStream for large objects - one key or many.
files.download(key, options?) · files.download(keys)
Reads an object. Returns a StoredFile by default (Blob-backed). Pass { as: "stream" } to opt into a ReadableStream for large objects.
const file = await files.download("avatars/abc.png");
// → StoredFile (Blob-backed)
const stream = await files.download("avatars/abc.png", { as: "stream" });
// → ReadableStreamByte ranges
Pass range to download only a contiguous slice of the object — the primitive behind video seeking and resumable downloads. Both bounds are 0-based, and end is inclusive, matching the HTTP Range header (bytes=start-end) this maps to. The returned StoredFile carries just the requested bytes, and its size is the range length, not the full object.
// Bytes 0–1023 (the first 1 KiB). end is inclusive, so this is 1024 bytes.
const head = await files.download("video.mp4", {
range: { start: 0, end: 1023 },
});
head.size; // 1024
// Omit end to read from an offset to the end — e.g. resume a download.
const rest = await files.download("video.mp4", { range: { start: 1024 } });
// Streaming works too, so you never buffer the whole slice.
const chunk = await files.download("video.mp4", {
as: "stream",
range: { start: 0, end: 65_535 },
});range combines with as: "stream", signal, timeout, and retries like any other download option.
Supported almost everywhere — the SDK threads the range through whatever primitive each provider exposes:
- Native byte ranges — AWS S3 and every S3-compatible adapter (Cloudflare R2 over HTTP, MinIO, DigitalOcean Spaces, Wasabi, Tigris, Backblaze B2, Storj, Hetzner, Akamai, Scaleway, OVHcloud, iDrive e2, Vultr, Filebase, Exoscale, Oracle Cloud, IBM COS, Tencent COS, Alibaba OSS, Yandex), Bun S3, Google Cloud Storage, Firebase Storage, Azure Blob, the R2 Workers binding (native
rangeoption), the localfsadapter, and the in-memory adapter. - HTTP
Rangeheader — UploadThing, Box, Vercel Blob (public), Cloudinary, PocketBase, Dropbox (via its temporary link), OneDrive, SharePoint, and Google Drive issue aRangerequest against the underlying URL/content endpoint.
For the HTTP-header adapters the SDK verifies the response came back 206 Partial Content and throws if the host answered 200 (i.e. ignored the range and sent the whole object), so a ranged read never silently transfers — and bills you for — the full file.
Throws a FilesError on adapters whose provider has no range primitive at all — Supabase, Appwrite, Netlify Blobs, Bunny Storage, Convex, and Vercel Blob private blobs (read through the SDK, which can't range). The SDK fails loudly rather than downloading the whole object and slicing it client-side, so the bandwidth saving is never quietly lost. Branch on the adapter's supportsRange flag to handle both at runtime:
const opts = files.adapter.supportsRange ? { range: { start, end } } : {};
const file = await files.download(key, opts);An out-of-shape range (start negative or non-integer, or end below start) throws before any provider call.
Many keys
Pass an array to download many in one call. Returns { downloaded, errors? } instead of throwing on partial failure; a missing key lands in errors. as applies to every download, and the call fans out with bounded concurrency (default 8) / stopOnError.
const result = await files.download(["avatars/a.png", "avatars/b.png"]);
result.downloaded; // StoredFile[] — successes, in the order supplied
result.errors; // undefined when every key succeeded