Amazon S3 refuses a single PutObject request larger than 5 GB. How does an S3 multipart upload work, and what does it give you beyond raising that ceiling?
answer
- three calls, plus one to abandon
- the UploadId ties the parts together
- nothing exists at the key until the end
- parts numbered 1 to 10,000
- 5 GB single PUT, 5 TiB assembled
basics
~10 sMultipart upload splits one object into parts sent independently: CreateMultipartUpload returns an UploadId, UploadPart sends each numbered piece, CompleteMultipartUpload assembles them. Beyond the 5 GB ceiling it adds parallelism, per-part retry, and resumable uploads.
solid answer
~40 sA multipart upload is a three-call protocol. `CreateMultipartUpload` opens the upload and returns an `UploadId`; you then send each chunk with `UploadPart`, tagging it with the `UploadId` and a `PartNumber` from 1 to 10,000, and S3 returns an ETag per part; finally `CompleteMultipartUpload` takes the list of part numbers and ETags and S3 stitches them into one object. That raises the maximum object size from 5 GB to 5 TiB, but the operational wins matter more: parts go up in parallel so you saturate the link, a failed part is retried on its own instead of restarting a 200 GB transfer, and an interrupted job can resume because the open upload survives. You rarely write these calls yourself — `aws s3 cp` and the SDK transfer managers do it automatically above a threshold.
code
bash · 15 lines# The CLI drives multipart for you; tune where the switch happens.
aws configure set default.s3.multipart_threshold 64MB
aws configure set default.s3.multipart_chunksize 64MB
aws s3 cp ./backup.tar s3://my-bucket/backups/backup.tar
# The same job with the raw three-call protocol.
UPLOAD_ID=$(aws s3api create-multipart-upload \
--bucket my-bucket --key backups/backup.tar \
--query UploadId --output text)
aws s3api upload-part --bucket my-bucket --key backups/backup.tar \
--upload-id "$UPLOAD_ID" --part-number 1 --body part-001
aws s3api list-parts --bucket my-bucket --key backups/backup.tar \
--upload-id "$UPLOAD_ID"go deeper
Be able to say the object goes up as numbered parts under an UploadId and only appears once you call complete, and that the CLI does this for you above 8 MB.
Explain the four calls including abort, the 1–10,000 part numbering, and that parts are billed while in flight even though no object is listed at the key.
Show the operational angle: resume via ListParts, why a failed part is cheap, why hand-rolled clients must parse the body of a 200 from CompleteMultipartUpload, and why ETag-based integrity checks break.
Own the ingest contract — who owns abandoned uploads, whether checksums are mandated for every large object, and whether teams write raw API calls or are required to go through a transfer manager.
## The ceiling that forces the issue A single `PutObject` request to Amazon S3 can carry at most 5 GB of data, and the request body is one HTTP entity: one connection, one shot, no resume. An object created through a multipart upload can be up to 5 TiB. So for anything genuinely large, multipart is not an optimization — it is the only way in. But the size ceiling is the least interesting reason to use it. A 4 GB single `PutObject` that dies at 96% has to start over from byte zero. That is the real problem multipart solves. ## The three-call protocol The API is deliberately small: 1. **`CreateMultipartUpload`** — you name the bucket and key and set the object's metadata (content type, storage class, encryption headers, ACL/ownership settings). S3 returns an **`UploadId`**, an opaque token identifying this in-flight upload. Note that metadata is fixed here, at the *start*, not at completion. 2. **`UploadPart`** — you send one chunk with the `UploadId` and a `PartNumber` between 1 and 10,000. S3 stores the chunk and returns an **ETag** for it. Parts may be uploaded in any order and from any number of machines or threads; the `PartNumber`, not the arrival order, determines position in the final object. Re-uploading the same `PartNumber` overwrites the previous attempt. 3. **`CompleteMultipartUpload`** — you send the ordered list of `{PartNumber, ETag}` pairs. S3 concatenates the parts, and only at that moment does the object become visible at its key. There is a fourth call, **`AbortMultipartUpload`**, which throws away an upload and its parts. `ListMultipartUploads` shows open uploads in a bucket and `ListParts` shows the parts already received for one of them — that pair is how a resuming client works out what it still needs to send. ```bash UP=$(aws s3api create-multipart-upload --bucket my-bucket --key big.bin \ --query UploadId --output text) aws s3api upload-part --bucket my-bucket --key big.bin \ --upload-id "$UP" --part-number 1 --body part1.bin ``` ## What exists while the upload is open This is the detail candidates most often miss. Between `CreateMultipartUpload` and `CompleteMultipartUpload`, the parts are **stored and billed**, but there is **no object**. `GetObject` on the key returns 404 (or the previous version of the object, if one exists — an in-flight multipart upload never partially overwrites what is already there). `ListObjectsV2` does not show the parts either; only `ListMultipartUploads` does. An upload that is never completed and never aborted sits there indefinitely, invisible in the object listing and visible only on the bill. Completion is atomic from a reader's point of view: the object appears whole or not at all. ## Why you use it below 5 GB too - **Parallelism.** One TCP stream rarely fills a fat pipe. Ten concurrent `UploadPart` calls usually do. - **Retry granularity.** A transient network failure costs you one part, not the whole transfer. - **Resumability.** The `UploadId` is durable, so a client can come back after a crash, call `ListParts`, and send only what is missing. - **Unknown length.** You can stream data whose total size you do not know in advance, deciding part by part when to stop. The AWS CLI and the SDK transfer managers switch to multipart automatically above a threshold — the CLI's default `multipart_threshold` is 8 MB — so `aws s3 cp` already does all of this for you. ## The ETag changes shape For a normal `PutObject` of an unencrypted object, the ETag is the MD5 of the content. For a multipart object it is **not**: it is a digest computed over the parts' digests, followed by a dash and the number of parts, like `"a1b2...-137"`. Any integrity check that assumes "ETag equals MD5 of my local file" breaks the moment a file crosses the multipart threshold. If you need end-to-end verification, use S3's checksum support — pass a `ChecksumAlgorithm` such as CRC32C or SHA256 — rather than reverse-engineering the ETag. ## One completion trap `CompleteMultipartUpload` can take a long time for a large object, so S3 keeps the connection alive and may return **HTTP 200 with an error document in the body**. A client that checks only the status code will treat a failed completion as a success. The AWS SDKs parse the body for you; hand-rolled HTTP clients frequently do not. ## Practical shape In interviews, the answer that lands is: three calls plus abort, parts numbered 1–10,000, nothing is visible until complete, the win is parallelism and retry granularity as much as size — and in real code you let the transfer manager drive it.
- If a client crashes halfway through a multipart upload and restarts, how does it avoid re-sending what it already uploaded?It persists the `UploadId`, then calls `ListParts` to see which `PartNumber` values S3 already holds and re-sends only the missing ones. Without the `UploadId` it can search `ListMultipartUploads` for an open upload on that key. Parts are addressed by number, so re-uploading a part simply replaces it.
- Can you change an object's storage class or content type when you call CompleteMultipartUpload?No. Object metadata — content type, storage class, encryption settings, ownership — is fixed at `CreateMultipartUpload`, before any part is sent. `CompleteMultipartUpload` only takes the part list. To change metadata afterwards you copy the object onto itself with the new headers, or abort and restart the upload.
- Why can't you rely on the ETag of a multipart object to verify your local file?A multipart ETag is a digest of the parts' digests plus a dash and the part count, not the MD5 of the whole object. Reproducing it requires knowing the exact part size and boundaries the uploader used. Use S3's `ChecksumAlgorithm` (CRC32C, SHA256, and so on) for real end-to-end integrity.
saying these in an interview costs you the question
- Thinks the object is readable while parts are still uploading
- Believes multipart is only for objects over 5 GB
- Assumes the ETag is always the MD5 of the object
- Thinks parts must be uploaded in order, sequentially
- Never mentions abort, so uploads leak silently