upload
Write a body to a key - a single object or many in one call, with optional progress tracking and multipart uploads.
files.upload(key, body, options?) · files.upload(items)
Writes a body to key. Accepts native File, Blob, ReadableStream, ArrayBuffer, or string. Content type is inferred from the input when possible.
await files.upload("avatars/abc.png", file, {
contentType: "image/png",
cacheControl: "public, max-age=31536000",
metadata: { userId: "123" },
});
// → { key, size, contentType, etag, lastModified }Options
Prop
Type
Progress tracking
Pass onProgress to drive a progress bar as bytes are sent:
await files.upload("big.zip", stream, {
onProgress: ({ loaded, total }) => {
const pct = total ? Math.round((loaded / total) * 100) : null;
console.log(pct === null ? `${loaded} bytes` : `${pct}%`);
},
});Every adapter calls onProgress. How fine-grained it is depends on the body and the adapter:
- A
ReadableStreambody is reported byte-by-byte on every adapter, as the bytes are consumed. Its length is unknown, sototalis omitted — you getloadedonly. - A buffered body (
File,Blob,ArrayBuffer,Uint8Array,string) is handed to the provider whole, so by default it reports{ loaded: 0, total }then{ loaded: total, total }. - Some adapters report true byte-level progress for every body type (buffered included) by tapping their SDK's native upload-progress hook: S3 and the S3-compatible adapters, R2 (HTTP), Azure Blob, Google Cloud Storage, Firebase Storage, Vercel Blob, and FTP. Notes:
- The S3 family (incl. R2 over HTTP) needs the optional
@aws-sdk/lib-storagepackage installed; it also enables multipart for large files. - GCS and Firebase Storage switch to a resumable upload when
onProgressis set (only that path emits progress) — one extra round trip versus the default simple upload.
- The S3 family (incl. R2 over HTTP) needs the optional
- The remaining adapters (Supabase, Convex, Dropbox, Box, OneDrive, Google Drive, SharePoint, Cloudinary, Bunny, Appwrite, PocketBase, Netlify Blobs, UploadThing, SFTP) send buffered bodies in a single request with no progress signal, so those report only the start/finish pair above. Stream bodies still get byte-level.
onProgress fires only while the upload is in flight and on success; a failed upload emits no final event, and a retry restarts progress. In the array form, each report also carries the item's key.
Multipart uploads
Pass multipart to upload a large body in parallel parts instead of a single request — the robust path for objects beyond the single-request limit (5 GB on S3) and for ReadableStream bodies of unknown length:
// Defaults: 5 MiB parts, 4 in flight.
await files.upload("backups/db.tar", stream, { multipart: true });
// Or tune it:
await files.upload("backups/db.tar", stream, {
multipart: { partSize: 16 * 1024 * 1024, concurrency: 8 },
});- S3 and the S3-compatible adapters (incl. R2 over HTTP) run multipart through the optional
@aws-sdk/lib-storagepackage, falling back to a singlePutObjectwhen the body fits in one part. Unknown-length streams use multipart automatically, even without the flag. - OneDrive uploads above 250 MB (and any
multipartrequest) go through a chunked upload session — large files that previously failed now just work. - GCS and Firebase Storage switch to a resumable upload;
partSizemaps to the chunk size. - Azure Blob already splits large bodies into parallel blocks;
multipartonly tunes the block size and concurrency. - Dropbox streams
ReadableStreambodies through its upload session chunk-by-chunk, so a large stream is never buffered whole;partSize(rounded to a 4 MiB multiple) tunes the chunk size. - Other adapters already stream natively or only accept a fully-buffered body, so they ignore the option.
Many items
Pass an array of { key, body, ...options } to upload many in one call. Each item carries its own contentType / cacheControl / metadata / multipart. The call returns a structured result instead of throwing on partial failure: successes land in uploaded, per-item failures (including invalid keys) in errors, both in the order supplied. It honors the client's prefix and fans out with bounded concurrency (default 8); stopOnError: true stops at the first failure.
const result = await files.upload(
[
{ key: "avatars/a.png", body: a, contentType: "image/png" },
{ key: "avatars/b.png", body: b },
],
{ concurrency: 8, stopOnError: false }
);
result.uploaded; // UploadResult[] — successes, in the order supplied
result.errors; // undefined when every item succeededItem (array form)
Prop
Type
Options (array form)
Prop
Type