# Multipart & chunked (/docs/adapters/multipart)



<ComponentPreview name="chunked-upload" />

`multipartAdapter` splits files into parts, uploads them in parallel, retries failed
parts independently, reports aggregate and chunk progress, and records finished parts
so uploads resume after a pause, a network loss, or a page reload.

You bring three functions; it works with S3's multipart API, GCS's XML API, Azure
block blobs, or your own chunk endpoint.

```ts
import { multipartAdapter, MiB } from "@uploadcn/core"

const adapter = multipartAdapter({
  partSize: 8 * MiB,
  concurrency: 4,
  retry: { retries: 3, baseDelay: 500 },
  create: ({ file }) => api.createUpload(file),                 // → session
  uploadPart: ({ session, partNumber, blob, signal, onProgress }) =>
    api.putPart(session, partNumber, blob, { signal, onProgress }), // → { etag }
  complete: ({ session, parts }) => api.complete(session, parts),    // → result
  abort: ({ session }) => api.abort(session),
  listParts: ({ session }) => api.listParts(session),           // optional
})
```

## Options [#options]

<TypeTable
  type="{
  partSize: { type: &#x22;number | (fileSize) => number&#x22;, default: &#x22;8 MiB&#x22; },
  minPartSize: { type: &#x22;number&#x22;, default: &#x22;5 MiB&#x22;, description: &#x22;Smallest part except the last (S3 limit).&#x22; },
  maxParts: { type: &#x22;number&#x22;, default: &#x22;10 000&#x22;, description: &#x22;Part size grows automatically to stay under this.&#x22; },
  concurrency: { type: &#x22;number&#x22;, default: &#x22;4&#x22;, description: &#x22;Parts in flight per file.&#x22; },
  retry: { type: &#x22;RetryOptions | false&#x22;, default: &#x22;3 retries from 500 ms&#x22;, description: &#x22;Per-part retry policy.&#x22; },
  create: { type: &#x22;(ctx) => Promise<TSession>&#x22;, required: true, description: &#x22;Session must be JSON-serializable (it's persisted).&#x22; },
  uploadPart: { type: &#x22;(ctx) => Promise<{ etag }>&#x22;, required: true },
  complete: { type: &#x22;(ctx) => Promise<TResult>&#x22;, required: true },
  abort: { type: &#x22;(ctx) => Promise<void>&#x22;, description: &#x22;Called when the user cancels or removes the item.&#x22; },
  listParts: { type: &#x22;(ctx) => Promise<UploadedPart[] | null>&#x22;, description: &#x22;Server truth on resume; return null to keep local state.&#x22; },
}"
/>

## Resume state [#resume-state]

After each finished part the adapter saves:

```ts
{ session, partSize, fileSize, parts: [{ partNumber, etag, size }] }
```

With [persistence](/docs/guides/resumable-uploads), this survives reloads. When the
saved state doesn't match the file (different size or part size), the upload starts
over cleanly.

## Retries at two levels [#retries-at-two-levels]

1. A failed **part** is retried with backoff without touching other parts.
2. If a part exhausts its retries, the **item** fails; the engine's own retry policy
   then retries the item, which resumes from the parts already stored.
