Skip to content
Files SDK
Esc
navigateopen⌘Jpreview
On this page

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

PropType
contentType?string

MIME type stored alongside the object and returned to readers in the `Content-Type` response header. Inferred from `File` / `Blob` `type` when not set; falls back to `application/octet-stream`.

Typestring
cacheControl?string

`Cache-Control` header stored on the object. Sent verbatim to the provider; controls how downstream caches and browsers cache reads of this key. **Throws** a {@link FilesError} on adapters with no cache-control field (FTP, SFTP, Dropbox, Box, OneDrive, SharePoint, Cloudinary, Appwrite, PocketBase, Bunny Storage, Convex, UploadThing, Bun's S3) rather than silently dropping it — check {@link Adapter.supportsCacheControl} to branch at runtime.

Typestring
metadata?Record<string, string>

Arbitrary user metadata stored alongside the object. Returned by `head()` and `list()` where the provider supports it. **Throws** a {@link FilesError} on adapters with no user-metadata primitive (Vercel Blob, UploadThing, FTP, SFTP, Dropbox, Box, OneDrive, SharePoint, Cloudinary, Appwrite, PocketBase, Bunny Storage, Convex, Bun's S3) rather than silently dropping it, mirroring the {@link DownloadOptions.range} gate. An empty object is treated as "no metadata" and never throws. Check {@link Adapter.supportsMetadata} to branch at runtime.

TypeRecord<string, string>
onProgress?(progress: UploadProgress) => void

Called as the upload makes progress, for driving a progress bar. Granularity depends on the body and the adapter: - A `ReadableStream` body is reported byte-by-byte as the adapter consumes it (`total` is omitted unless the length is known). - A buffered body (`File`, `Blob`, `ArrayBuffer`, `Uint8Array`, `string`) is handed to the provider whole, so it reports `{ loaded: 0, total }` then `{ loaded: total, total }` — unless the adapter reports true progress itself (see below). - **S3 and the S3-compatible adapters** report true byte-level progress for every body type (including multipart for large files). This path uses `@aws-sdk/lib-storage`, an optional peer dependency that must be installed when `onProgress` is used with those adapters. Only fires while the upload is in flight and on success; a failed upload does not emit a final event. On retry, progress restarts.

Type(progress: UploadProgress) => void
multipart?boolean | MultipartOptions

Upload the body in parallel parts instead of a single request. Pass `true` for sensible defaults (5 MiB parts, 4 in flight), or an object to tune `partSize` / `concurrency`. Multipart is the robust path for large objects and for `ReadableStream` bodies of unknown length: a single PUT must buffer or know the length up front, while multipart streams part-by-part. On S3-family adapters this routes through `@aws-sdk/lib-storage` (an optional peer dependency) and is **auto-engaged for unknown-length streams** even when this flag is unset. OneDrive, GCS, Firebase, and Azure map it to their native chunking; other adapters already stream or chunk transparently and ignore it.

Typeboolean | MultipartOptions
control?UploadControl

Drive the upload through a pause-able, resumable session. Construct an {@link UploadControl}, pass it here, and call `pause()` / `resume()` / `abort()` on it; persist `control.toJSON()` and rehydrate with `UploadControl.from(token)` to resume in a later process. Requires a body with a known length (`File`, `Blob`, `ArrayBuffer`, a typed array, or `string`) — a `ReadableStream` can't be re-read to resume. Supported on S3 and the S3-compatible adapters, GCS, Firebase Storage, Azure Blob, OneDrive, and Dropbox; other adapters throw. Not available in the array (bulk) form of `upload`.

TypeUploadControl
signal?AbortSignal

Abort the operation when this signal is aborted. When both constructor and per-call signals are provided, either one can abort the call.

TypeAbortSignal
timeout?number

Overall timeout in milliseconds, applied to each attempt. A timeout aborts the operation and is not retried. `0` or a negative value disables timeout handling.

Typenumber
retries?RetryOptions

Retry provider failures. A number is treated as `{ max: number }`.

TypeRetryOptions

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 ReadableStream body is reported byte-by-byte on every adapter, as the bytes are consumed. Its length is unknown, so total is omitted — you get loaded only.
  • 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-storage package installed; it also enables multipart for large files.
    • GCS and Firebase Storage switch to a resumable upload when onProgress is set (only that path emits progress) — one extra round trip versus the default simple upload.
  • 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-storage package, falling back to a single PutObject when the body fits in one part. Unknown-length streams use multipart automatically, even without the flag.
  • OneDrive uploads above 250 MB (and any multipart request) go through a chunked upload session — large files that previously failed now just work.
  • GCS and Firebase Storage switch to a resumable upload; partSize maps to the chunk size.
  • Azure Blob already splits large bodies into parallel blocks; multipart only tunes the block size and concurrency.
  • Dropbox streams ReadableStream bodies 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.

Pause and resume

Pass a control (UploadControl) to pause, resume, or abort a large upload — and to resume it later, even in a new process, from a serializable session token. It’s supported on every adapter whose provider exposes a resumable session — S3 and the S3-compatible adapters, GCS, Firebase Storage, Google Drive, Azure, OneDrive, Dropbox, Vercel Blob, the local filesystem, FTP/SFTP, Supabase, Appwrite, and Cloudinary (Box, bun-s3, and memory pause in-process only); the rest throw. See Resumable uploads for the full walkthrough.

import { UploadControl } from "files-sdk";

const control = new UploadControl();
const result = files.upload("big.iso", file, { control });
control.pause();
control.resume();
await result;

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 succeeded

Item (array form)

PropType
keystring
Typestring
bodyBody
TypeBody
contentType?string

Per-item MIME type. See {@link UploadOptions.contentType}.

Typestring
cacheControl?string

Per-item `Cache-Control`. See {@link UploadOptions.cacheControl}.

Typestring
metadata?Record<string, string>

Per-item user metadata. See {@link UploadOptions.metadata}.

TypeRecord<string, string>
multipart?boolean | MultipartOptions

Per-item multipart toggle/tuning. See {@link UploadOptions.multipart}.

Typeboolean | MultipartOptions

Options (array form)

PropType
onProgress?(progress: UploadProgress & { key: string }) => void

Called as each item makes progress. Same semantics as {@link UploadOptions.onProgress}, with the item's `key` added so callers can attribute the report to a file when several upload concurrently.

Type(progress: UploadProgress & { key: string }) => void
concurrency?number

How many per-key operations run in parallel. Defaults to `8`. Ignored when `stopOnError` is set — that path runs sequentially.

Typenumber
stopOnError?boolean

When `true`, stop at the first failure and return immediately with the results gathered so far plus that error. When `false` (default), process every item and collect per-key failures in `errors`.

Typeboolean

Was this page helpful?