10.12
Paths to S3 (3/3): Multipart, Pre-signed URLs and Batch Operations
How applications push big files, let users upload without credentials, and change millions of objects at once, plus a chooser across all 13 paths.
26 min Reading + animations
Tip: tap or hover any dotted-underlined term to see what it means.
By the end of this lesson you will be able to
- Tell apart the three paths that start inside your own application: multipart upload, pre-signed URLs and S3 Batch Operations, and say which problem each one solves.
- Walk through a multipart upload call by call, calculate part sizes and part counts within the limits, and stop abandoned uploads from costing money.
- Explain whose permissions a pre-signed URL carries, how long it can live with each kind of credential, and why it fails before its expiry; design a browser upload that never passes through your servers.
- Build a Batch Operations job from a manifest, an operation, an IAM role and a report, and handle a job that finishes with some failures.
- Choose among all 13 paths to S3 from where the data starts, how much there is, how often it moves and what matters most, and recognise each one from its exam keywords.
#Three paths that start in your application
#Where 11–13 fit among the 13 paths
This is the last of three lessons that collect every way data travels into, out of or inside S3, so you can stop mixing them up:
- Paths 1–5 (lesson 10.10) are network routes: how packets from a VPC or a data centre reach S3 privately. Gateway endpoint, interface endpoint, VPC Peering, Transit Gateway, and Site-to-Site VPN or Direct Connect.
- Paths 6–10 (lesson 10.11) are transfer services: tools that move large amounts of data for you. S3 File Gateway, DataSync, Snowball, Transfer Acceleration and Transfer Family.
- Paths 11–13 (this lesson) are things your own code does with the S3 API. Nothing to deploy, no network change:
- 11. Multipart upload: how one big object gets in, in pieces.
- 12. Pre-signed URL: how someone without AWS credentials gets one object in or out.
- 13. S3 Batch Operations: how millions of objects already in S3 get changed in one go.
The three answer three different questions, and the exam keeps them apart by keyword: "large file, unreliable network" is 11, "users upload directly from the browser" is 12, "millions of existing objects" is 13. They also combine with the other ten: a multipart upload can travel through a gateway endpoint, each of its parts can be pre-signed, and its traffic can be accelerated.
Some of this is taught from another angle elsewhere in the chapter: multipart for speed in 10.6, Batch Operations among the bucket tools in 10.7, pre-signed URLs as a security control in 10.9. This lesson is complete on its own, from the "path" point of view: where the bytes start, who sends them, and why you would pick this path over the others.
#Path 11: multipart upload
#Multipart: what it is and why it exists
A normal upload is one PutObject request: one HTTP request carrying the whole object. That has two hard problems for big files:
- Size limit. A single PUT can carry at most 5 GB.
- All or nothing. If the connection drops at 95% of a 4 GB upload, the request fails and you start again from zero. On a phone or a long intercontinental link, a big single request may never finish.
Multipart upload splits one object into parts that are uploaded separately, in any order and in parallel, and then joined by S3 into a single object. A failed part is sent again on its own. The owner’s notes put the rule simply, and AWS agrees: consider multipart from about 100 MB, and you must use it above 5 GB. The largest object S3 stores is 5 TB in the exam (see the side note below for the 2025 change).
#How it works, step by step
1. CreateMultipartUpload
You tell S3 the bucket, the key and the object’s settings (storage class, encryption, metadata, tags). S3 returns an UploadId, which every later call must quote.
2. UploadPart, many times, in parallel
Each request carries one part with a part number from 1 to 10,000. S3 stores the part and returns its ETag; your code keeps a list of part numbers and ETags. Parts can arrive in any order, from several threads or even several machines. Uploading the same part number again simply replaces that part, which is how a retry works.
3. CompleteMultipartUpload
You send the list of part numbers with their ETags. S3 assembles the parts in part-number order and the object appears atomically: before this call nobody can read it, after it everybody can. The object’s ETag ends in
-N, the number of parts, which is why the ETag of a multipart object is not the MD5 of the file.Or: AbortMultipartUpload
If you give up, abort with the UploadId. S3 deletes the stored parts and you stop paying for them.
ListPartsshows the parts of one upload, andListMultipartUploadsshows every upload in progress in a bucket.
You rarely write these calls by hand. The AWS CLI (aws s3 cp) and the SDKs’ transfer managers switch to multipart automatically above a threshold (8 MB by default in the CLI), choose a part size, run several parts at once (10 by default in the CLI) and retry failed parts.
Multipart upload: 8 GB from a phone
A travel-video app uploads an 8 GB file as 8 parts of 1 GB over mobile data. Watch the parts, the API calls, and what S3 is storing and billing for.
Scenario
Start, send parts in parallel, complete.
Part 1
waiting
Part 2
waiting
Part 3
waiting
Part 4
waiting
Part 5
waiting
Part 6
waiting
Part 7
waiting
Part 8
waiting
Last call
CreateMultipartUpload → UploadId
Parts stored (billed)
0 GB
Object in the bucket
not yet
The app asks S3 to start a multipart upload for the key videos/trip.mp4. S3 answers with an UploadId; every later call quotes it.
#Part limits and the arithmetic
| Limit | Value |
|---|---|
| Part size | 5 MiB to 5 GiB; the last part can be smaller than 5 MiB |
| Number of parts | 1 to 10,000 per upload |
| Largest object | 5 TB in the exam; 50 TB since December 2025 (48.8 TiB = 10,000 × 5 GiB) |
| Single PUT (no multipart) | up to 5 GB |
Copy in one CopyObject | up to 5 GB; bigger objects are copied with multipart (UploadPartCopy) |
The 10,000-part limit decides the smallest part size you can use for a given object:
- An 8 GB video with 8 MiB parts: 8,192 MiB ÷ 8 MiB = 1,024 parts. Fine.
- A 100 GiB database dump with the 5 MiB minimum would need 20,480 parts, too many. The part size must be at least 100 GiB ÷ 10,000 ≈ 10.24 MiB, so you would choose 16 MiB (6,400 parts).
- A 1 TiB file needs parts of at least 1,048,576 MiB ÷ 10,000 ≈ 105 MiB.
The CLI and SDKs raise the part size automatically when a file would exceed 10,000 parts; your own code must do the same.
#Retrying one part, and the parallel speed-up
Two different benefits come from the same design:
- Resilience. A broken connection costs one part, not the whole object. With 16 MiB parts on a flaky mobile link, a drop wastes at most 16 MiB. This is the owner’s exam trick: "upload fails because of network issues" → multipart, so only the failed part is uploaded again. An upload can even be paused for days and resumed, because the stored parts wait in S3 until you complete or abort.
- Throughput. One TCP stream rarely fills a fast link, especially over long distances. Ten parts in flight at once fill it far better. Lesson 10.6 covers this angle, along with byte-range fetches, which do the same trick for downloads.
Smaller parts mean cheaper retries but more requests (each part is one PUT request on the bill); larger parts mean fewer requests but more to resend. Between 8 and 100 MiB is a common choice.
#Abandoned uploads still cost money
Parts of an upload that was never completed or aborted are stored and billed at the storage class you chose, but they are not objects: a bucket listing does not show them, and the console’s object list looks empty. A mobile app that loses a thousand uploads a day quietly builds up terabytes of invisible, billed parts.
The fix is a lifecycle rule on every bucket that receives multipart uploads: AbortIncompleteMultipartUpload after a number of days since the upload started (7 is common). S3 then aborts the stale uploads and deletes their parts. Lesson 10.5 covers lifecycle rules; S3 Storage Lens (10.7) shows how many bytes of incomplete uploads each bucket holds.
#Why multipart and not the alternatives
| Alternative | Why multipart wins here |
|---|---|
A single PutObject | Capped at 5 GB, and one drop restarts everything |
| Transfer Acceleration (path 9) | It speeds up the route, not the failure handling; use it with multipart for far-away users |
| DataSync (path 7) or Snowball (path 8) | Built for moving whole file systems from a data centre, not for one app uploading one file from a phone |
| Splitting the file into many small objects yourself | You would have to rejoin them on every read; multipart gives you one real object |
#Path 12: pre-signed URLs
#Pre-signed URL: what it is and why it exists
Your bucket is private, as it should be. Now a customer must download one invoice, or a seller must upload one product photo. Neither has AWS credentials, and you do not want to make the bucket public or create an IAM user for every customer.
A pre-signed URL is a normal S3 URL with a signature in its query string. Someone who has permission (your backend) signs a request for one object, one method, until one time; anyone who holds the URL can then perform exactly that request, without AWS credentials, until it expires. It is a permission slip: "the bearer may PUT sellers/42/photo.jpg until 10:05, signed by the shop’s backend."
#Pre-signed GET and PUT
- GET (download): "let this logged-in student watch this paid video for an hour", "let this customer download this invoice". The URL can be pasted into a browser, an
<img>tag or a video player. - PUT (upload): "let this seller upload one photo to exactly this key". The browser sends the file in the body of an HTTP PUT. If an object with the same key exists, it is replaced, so let the backend choose unique keys (a UUID per upload).
- Other methods can be signed too (
HEADto read metadata,UploadPartfor each part of a multipart upload,DELETE).
You can create pre-signed URLs from the S3 console (share an object), the AWS CLI (aws s3 presign, which signs GET only) and the SDKs (any method, including PUT). Creating one is local maths: the SDK computes an HMAC with the signer’s secret key and makes no call to AWS. That is why you can "successfully" create a URL for an object that does not exist, or with credentials that have no permission. The mistake shows up only when the URL is used.
#Whose permissions the URL carries
When S3 receives a pre-signed request, it treats it as a request made by the signer, with the signer’s permissions at that moment. The owner’s notes say the same: the user inherits the permissions of the URL’s creator. Three consequences:
- The signer must be allowed to do the action. A backend role without
s3:PutObjecton that key signs URLs that always fail with 403. - Revoking the signer revokes the URLs. Remove the permission, delete or deactivate the IAM user’s access key, or let a role session end, and every URL it signed stops working, even before its own expiry.
- It is a bearer token. Anyone who gets the URL can use it, as many times as they like, until it expires. Treat it like a password: short expiries, HTTPS only, never logged.
Block Public Access does not affect pre-signed URLs, because the request is signed: S3 sees an authenticated request from the signer, not an anonymous one. Bucket policies can still restrict them, for example deny when s3:signatureAge is too old, or when the request does not come from your network (aws:SourceIp, or aws:SourceVpce through an endpoint).
#How long a URL can live
| How it is created | Expiry you can set | What really limits it |
|---|---|---|
| S3 console | 1 minute to 12 hours | The console session’s credentials |
AWS CLI aws s3 presign | --expires-in in seconds, default 3,600 (1 hour), max 604,800 (7 days) | The credentials used |
| SDK, with IAM user long-term keys (Signature Version 4) | up to 7 days | Deleting or deactivating the key |
| SDK, with temporary credentials (an assumed role, an EC2 instance profile, a Lambda execution role, IAM Identity Center) | up to 7 days on paper | The session’s end: the URL dies with the credentials, often after 1 to 12 hours (instance profile credentials last about 6 hours) |
S3 checks the expiry when a request starts. A 2 GB download that began one second before the expiry finishes; if the connection drops and the player reconnects after the expiry, it fails.
#Uploading from the browser straight to S3
The pattern every photo, document and video site uses:
- The browser asks your backend for an upload URL, sending only the file name, type and size.
- The backend checks the user, decides the key (
sellers/42/2f1c….jpg), signs a PUT URL valid for a few minutes, and returns it. - The browser sends the file straight to S3 with that URL. The bucket needs a CORS rule allowing
PUTfrom your site’s origin (lesson 10.3). - S3 stores the object; an S3 event (lesson 10.7) starts thumbnails or virus scanning.
Your servers carry a few hundred bytes of JSON instead of the file. They no longer need bandwidth, memory or time for uploads, and S3 absorbs any spike. For big files, combine it with path 11: the backend creates the multipart upload, pre-signs each UploadPart, and completes it when the browser reports the ETags.
Pre-signed URL: the browser talks to S3 directly
A seller on a shop site uploads a product photo. The backend holds the AWS credentials; the browser never sees them. Compare the old way, the pre-signed way, and the two ways a URL stops working.
Scenario
Without a pre-signed URL every byte passes through your server twice.
The browser sends the whole photo to the backend. The server spends bandwidth, memory and a request slot on bytes it does not even look at.
#Pre-signed POST: an HTML form with rules
A pre-signed PUT URL fixes the key and the method, but it cannot say "at most 10 MB" or "only images". For that, S3 offers pre-signed POST: the backend signs a policy document with conditions, and the browser uploads with an ordinary HTML form (multipart/form-data). Typical conditions:
content-length-rangefrom 1 byte to 10 MB;- the key must start with
sellers/42/; Content-Typemust start withimage/;- an expiry for the policy.
S3 rejects any upload that breaks a condition, before storing it. Use POST when uploaders are untrusted and you must cap size and type; PUT is simpler when your own app code controls the file.
#Why a pre-signed URL and not the alternatives
| Alternative | Why the pre-signed URL wins here |
|---|---|
| Make the bucket or a prefix public | Anyone could read (or write) everything, for ever; one leak exposes all customers |
| An IAM user per customer | Thousands of identities and secrets to manage, and the 5,000-user quota per account |
| Proxy uploads through your servers | Your servers carry every byte twice and must scale for upload traffic |
| Cognito identity pools with temporary credentials (chapter 15) | Good for apps that call many AWS APIs; overkill for "upload one file" |
| CloudFront signed URLs (chapter 11) | For delivery through the CDN, many files and caching; S3 pre-signed URLs are for one object straight from S3, including uploads |
| Transfer Family (path 10) | For partners with SFTP clients, not browsers |
Self-checkQuestion
A backend running on EC2 signs S3 pre-signed GET URLs with its instance profile and asks for a 7-day expiry. Customers report that links sent in the morning fail by the evening. What explains it?
#Path 13: S3 Batch Operations
#Batch Operations: what it is and why it exists
Paths 1–12 bring data into S3 (or out of it). Path 13 works on data that is already there. Sooner or later every large bucket needs a change applied to every object: new tags for cost allocation, encryption for objects written years ago, a copy to a new account, a restore from Glacier, a custom process on each file.
You could write a script: list the bucket, loop over the keys, call the API, handle throttling, retry failures, remember where you stopped when the laptop sleeps, and produce a list of what failed. For 300 million objects that script runs for days and fails in creative ways. S3 Batch Operations does the same work as a managed service: one request starts a job that runs one operation on every object in a list, with retries, progress tracking, notifications and a completion report. The owner’s exam trick: "perform an action on millions of existing objects", "change tags for billions of files" → Batch Operations, not a custom script or Lambda loop.
#The job: manifest, operation, role, report
A job has four main parts:
- Manifest: the list of objects. Three ways to give it:
- an S3 Inventory report (CSV, via its
manifest.json); Inventory lists every object daily or weekly, and you can query it with Athena (chapter 17) to filter, for example "all objects over 1 MB without encryption"; - a CSV file you write:
bucket,keyorbucket,key,versionIdper line, keys URL-encoded; - a generated list: the job builds it from filters (prefix or suffix, creation date, size, storage class, encryption type) and can save it as a manifest.
- an S3 Inventory report (CSV, via its
- Operation: exactly one per job, with its parameters (the tag set, the destination bucket, the restore tier…).
- IAM role: the job runs as this role. It trusts
batchoperations.s3.amazonaws.comand needs the operation’s permissions (for a copy: read on the source, write on the destination), plus read on the manifest and write on the report bucket. - Completion report (optional, recommended): written to a bucket you choose, for all tasks or failed tasks only, with each key’s status and error code.
Add a priority (higher numbers run first among your jobs in that Region) and, for jobs created in the console, a confirmation step: the job waits in Suspended until you confirm it.
#The operations it can run
| Operation | Typical use |
|---|---|
| Copy | Copy objects to another bucket, Region or account; change storage class or metadata; copy in place to encrypt existing objects. Objects up to 5 GB; the job is created in the destination Region |
| Invoke AWS Lambda function | Anything custom per object: watermark, transcode, scan, rename |
| Replace all object tags / Delete all object tags | Cost allocation, lifecycle filters, access control by tag |
| Replace access control list (ACL) | Clean up old ACLs before disabling them |
| Restore | Bring many objects back from S3 Glacier Flexible Retrieval or Deep Archive |
| Object Lock retention / legal hold | Put a retention date or a legal hold on many objects (lesson 10.9) |
| Replicate (Batch Replication) | Replicate existing objects, which live replication ignores (lesson 10.4) |
| Compute checksums | Compute checksums of stored objects without downloading them |
| Update object encryption | Change existing objects from SSE-S3 to SSE-KMS, to another KMS key, or to Bucket Keys, without rewriting the data (new in January 2026) |
#Job lifecycle, retries and partial failures
A job moves through New → Preparing → (Suspended →) Ready → Active → Complete, or ends in Cancelled or Failed. A higher-priority job can pause a lower one.
- Tasks fail individually. An object deleted since the inventory was taken, an object over 5 GB in a copy, a KMS key the role cannot use: each is one failed task, recorded in the report, and the job carries on. Temporary errors such as throttling are retried automatically.
- The job fails as a whole only if, after at least 1,000 tasks, more than 50% have failed. That usually means a wrong role, a wrong bucket or a wrong manifest, and stopping early saves you money.
- Not a snapshot. The job acts on the latest version of each key when it gets to it. If objects can change during a long job, put version IDs in the manifest.
- Fixing failures. The report lists every failed key with its error. Feed that list back as the manifest of a small second job.
- Cost. About $0.25 per job plus $1.00 per million objects processed, on top of the normal request charges of the operation (and Lambda charges if you invoke a function). 300 million objects ≈ $300 plus requests.
An S3 Batch Operations job
One request runs one operation on every object in a list. Batch Operations reads the list, assumes your role, retries, and writes a report of what worked and what did not.
Scenario
A photo company must tag every object with its cost centre.
Job
| Status | Done | Failed |
|---|---|---|
| — | 0 | 0 |
S3 Inventory writes a daily CSV list of every object in the bucket. That list, or a CSV you write yourself (bucket, key, optional version ID), is the manifest.
#Why Batch Operations and not the alternatives
| Alternative | Why Batch Operations wins here |
|---|---|
| A script that lists and loops | You would build retries, throttling, checkpoints and a report yourself, and run a machine for days |
| Lifecycle rules (lesson 10.5) | They only transition or expire by age, prefix and tags, and apply in the background; they cannot retag, copy to another account, restore or run code |
| S3 event notifications (lesson 10.7) | They fire for new events, not for objects that already exist |
| Live replication (lesson 10.4) | Copies only new writes; existing objects need Batch Replication, which is a Batch Operations job |
| DataSync (path 7) | Moves data between storage systems; it does not tag, restore or run Lambda on objects in place |
#The three side by side
| 11. Multipart upload | 12. Pre-signed URL | 13. Batch Operations | |
|---|---|---|---|
| Problem it solves | One big object, unreliable or slow network | Someone without AWS credentials must GET or PUT one object | Millions of existing objects need the same change |
| Who sends the bytes | Your app (or a browser with pre-signed parts) | The end user’s browser or device, straight to S3 | S3 itself, inside AWS |
| Key API | CreateMultipartUpload, UploadPart, Complete, Abort | Any S3 request, signed in advance | CreateJob (S3 Control) |
| Limits to remember | 5 MiB–5 GiB parts, 10,000 parts, 5 TB object, single PUT 5 GB | Console 12 h, CLI/SDK 7 days with IAM user keys, session length with roles | One operation per job, Copy ≤ 5 GB, fails if >50% of tasks fail |
| Cost trap | Incomplete uploads billed until aborted | None, but a leaked URL is a leaked permission | $0.25/job + $1 per million objects + requests |
| Exam keyword | "large files", "network failures", "over 5 GB" | "temporary access", "upload without credentials" | "millions of existing objects", "billions of files" |
#Choosing among all 13 paths
#The comparison table
| # | Path | Where data starts | Online / offline | One-off / ongoing | Protocol | Cost hint | Typical exam keyword |
|---|---|---|---|---|---|---|---|
| 1 | Gateway endpoint | EC2, Lambda in a VPC, same Region | Online, private | Ongoing | S3 API (HTTPS) via route table | Free | "private subnet, no NAT", "without traversing the internet", cheapest |
| 2 | Interface endpoint | VPC, peered VPC, on-premises over DX/VPN | Online, private | Ongoing | S3 API via private IPs (PrivateLink) | Per hour per AZ + per GB | "access S3 from on-premises privately", private IP |
| 3 | VPC Peering | Another VPC (to a shared endpoint) | Online, private | Ongoing | Routing between two VPCs | Data transfer only | two VPCs, not transitive |
| 4 | Transit Gateway | Many VPCs and accounts | Online, private | Ongoing | Hub routing to a central endpoint | Per attachment hour + per GB | "hundreds of VPCs", centralised endpoints |
| 5 | Site-to-Site VPN / Direct Connect | On-premises data centre | Online, private | Ongoing | IPsec over internet / dedicated line | VPN per hour; DX port hours + transfer | "consistent bandwidth", "private connection from on-premises" |
| 6 | S3 File Gateway | On-premises apps using file shares | Online | Ongoing | NFS / SMB in front, S3 behind | Gateway + S3 requests | "keep using NFS/SMB", hybrid, local cache |
| 7 | DataSync | On-premises or other clouds’ storage | Online | One-off or scheduled | NFS, SMB, HDFS, object storage → S3 | Per GB copied | "migrate online", "scheduled sync", "preserve metadata" |
| 8 | Snowball | Data centre or remote site | Offline | One-off | Device shipped by courier | Per device job + days | "petabytes, limited bandwidth", "weeks over the network" |
| 9 | Transfer Acceleration | Users or sites far from the bucket | Online, public | Ongoing | HTTPS via edge locations | Extra per GB, only when faster | "global users upload", long distance |
| 10 | Transfer Family | Partners’ systems | Online | Ongoing | SFTP, FTPS, FTP, AS2 | Per endpoint hour + per GB | "partners use SFTP", no change for partners |
| 11 | Multipart upload | Any app with a big file | Online | Per file | S3 API in parts | Per part request; incomplete parts billed | "large files", "unreliable network", "> 5 GB" |
| 12 | Pre-signed URL | Users’ browsers and devices | Online, public | Per object | HTTPS GET / PUT / POST, signed | Normal request costs | "upload directly from browser", "temporary access" |
| 13 | Batch Operations | Already in S3 | Inside AWS | One-off jobs | S3 Control job | $0.25/job + $1 per million objects | "millions of existing objects", "billions of files" |
Read the table from left to right as questions: where is the data, does it have to stay private, how much and how often, and what speaks the protocol at the other end. Lessons 10.10 and 10.11 teach paths 1–10 in detail; chapters 12 (hybrid storage), 22 (advanced networking) and 23 (migration) go deeper still.
#The 13-path chooser
The 13-path chooser
Answer four questions. The best path lights up in full colour, useful companions are outlined, and each one links to where it is taught.
1. Where does the data start?
2. How much?
3. How often?
4. What matters most?
All 13 paths
End users have no AWS credentials and you do not want your servers to carry their bytes. The backend signs a short pre-signed URL and the browser or phone talks to S3 directly. Big files: pre-sign each UploadPart of a multipart upload. Users far from the bucket: combine with Transfer Acceleration.
- Best fit12. Pre-signed URL: A time-limited link that lets someone without AWS credentials GET or PUT one object, acting as the signer. Go to the lesson →
Exam keywords: users upload directly from browser, temporary access, without AWS credentials, private bucket - Also useful11. Multipart upload: One big object sent as up to 10,000 parts in parallel; a failed part is retried alone. Go to the lesson →
Exam keywords: files over 100 MB, required over 5 GB, unreliable network, resume, parallel
#Keywords that give the answer away
- "from a private subnet without NAT", "without traversing the internet", same Region, cheapest → gateway endpoint (1).
- "from on-premises over Direct Connect, privately", "private IP for S3" → interface endpoint (2) behind VPN / Direct Connect (5).
- "many VPCs share one endpoint" → interface endpoint in a hub VPC + Transit Gateway (4), or peering (3) for two VPCs.
- "applications must keep using NFS or SMB shares" → S3 File Gateway (6).
- "migrate file servers online", "scheduled copy", "keep permissions and timestamps" → DataSync (7).
- "petabytes", "limited bandwidth", "would take weeks", "no connectivity" → Snowball (8).
- "users around the world upload to one bucket slowly" → Transfer Acceleration (9).
- "partners use SFTP", "AS2", "cannot change their clients" → Transfer Family (10).
- "large file", "network interruptions", "over 5 GB" → multipart upload (11).
- "users upload directly from the browser", "temporary access to a private object" → pre-signed URL (12).
- "millions of existing objects", "retag / re-encrypt / restore in bulk" → Batch Operations (13).
#Paths 11–13 in system design
- Never let your app servers carry user files. Browser → pre-signed URL → S3 → event → processing is the standard upload pipeline. It removes upload bandwidth from your scaling problem and keeps the bucket private.
- Combine paths, do not choose just one. A global video app uses pre-signed multipart parts (12 + 11) with Transfer Acceleration (9); a backend in a private subnet sends its own multipart uploads through a gateway endpoint (11 + 1).
- Put an abort rule on every upload bucket. It costs nothing and prevents a silent, growing bill.
- Keep pre-signed URLs short and signed on demand, by a role with the narrowest permission (only
s3:PutObjectonuploads/*), and adds3:signatureAgeor network conditions in the bucket policy for sensitive data. - Plan bulk changes as jobs, not scripts. Turn on S3 Inventory before you need it; filter it with Athena; run Batch Operations with a report; re-run the failed list. Test on a small manifest first; in the console the confirmation step is your last chance.
- Use version IDs in manifests when objects can change while a long job runs.
#Try it yourself
Multipart by hand (the CLI does this for you with aws s3 cp):
# 0. Make a 40 MB test file and split it into 8 MiB parts (the last part is smaller)
dd if=/dev/urandom of=big.bin bs=1M count=40
split -b 8M big.bin part-
# 1. Start the upload and keep the UploadId
UPLOAD_ID=$(aws s3api create-multipart-upload --bucket uploads-example \
--key videos/big.bin --query UploadId --output text)
# 2. Upload the parts (each returns an ETag)
aws s3api upload-part --bucket uploads-example --key videos/big.bin \
--part-number 1 --body part-aa --upload-id "$UPLOAD_ID"
# ...parts 2 to 5 the same way, then list what S3 has:
aws s3api list-parts --bucket uploads-example --key videos/big.bin --upload-id "$UPLOAD_ID"
# 3. Complete with the part numbers and ETags (parts.json: {"Parts":[{"PartNumber":1,"ETag":"..."},...]})
aws s3api complete-multipart-upload --bucket uploads-example --key videos/big.bin \
--upload-id "$UPLOAD_ID" --multipart-upload file://parts.json
# Find and abort forgotten uploads
aws s3api list-multipart-uploads --bucket uploads-example
aws s3api abort-multipart-upload --bucket uploads-example --key videos/old.bin --upload-id EXAMPLEID
# The safety net: abort incomplete uploads after 7 days
aws s3api put-bucket-lifecycle-configuration --bucket uploads-example --lifecycle-configuration \
'{"Rules":[{"ID":"abort-mpu","Status":"Enabled","Filter":{},"AbortIncompleteMultipartUpload":{"DaysAfterInitiation":7}}]}'
# Let the high-level CLI choose parts and run them in parallel
aws configure set default.s3.multipart_chunksize 16MB
aws configure set default.s3.max_concurrent_requests 10
aws s3 cp ./trip-video.mp4 s3://uploads-example/videos/
Pre-signed URLs:
# A GET URL valid for 10 minutes (the CLI signs GET only)
aws s3 presign s3://invoices-example/2026/10/inv-1042.pdf --expires-in 600
curl -o inv.pdf "<the URL>" # works without AWS credentials
sleep 601; curl -I "<the URL>" # 403 after expiry
# PUT and POST need an SDK (boto3 shown)
import boto3, uuid
s3 = boto3.client("s3")
key = f"sellers/42/{uuid.uuid4()}.jpg"
put_url = s3.generate_presigned_url(
"put_object",
Params={"Bucket": "photos-example", "Key": key, "ContentType": "image/jpeg"},
ExpiresIn=300,
)
# Browser: fetch(put_url, {method: "PUT", headers: {"Content-Type": "image/jpeg"}, body: file})
post = s3.generate_presigned_post(
"photos-example", key,
Fields={"Content-Type": "image/jpeg"},
Conditions=[["content-length-range", 1, 10 * 1024 * 1024],
["starts-with", "$Content-Type", "image/"]],
ExpiresIn=600,
)
# post["url"] and post["fields"] go into an HTML form with the file input last
Batch Operations (S3 Control API):
# A role Batch Operations can assume
aws iam create-role --role-name s3-batch-tagging --assume-role-policy-document \
'{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"batchoperations.s3.amazonaws.com"},"Action":"sts:AssumeRole"}]}'
# A job that tags every object listed in a CSV manifest, with a report of failures
aws s3control create-job --account-id 111122223333 --region us-east-1 \
--operation '{"S3PutObjectTagging":{"TagSet":[{"Key":"desk","Value":"sports"}]}}' \
--manifest '{"Spec":{"Format":"S3BatchOperations_CSV_20180820","Fields":["Bucket","Key"]},"Location":{"ObjectArn":"arn:aws:s3:::manifests-example/sports.csv","ETag":"EXAMPLEETAG"}}' \
--report '{"Bucket":"arn:aws:s3:::reports-example","Prefix":"batch","Format":"Report_CSV_20180820","Enabled":true,"ReportScope":"FailedTasksOnly"}' \
--priority 10 --role-arn arn:aws:iam::111122223333:role/s3-batch-tagging \
--no-confirmation-required
aws s3control describe-job --account-id 111122223333 --job-id <job-id>
aws s3control list-jobs --account-id 111122223333 --job-statuses Active Complete
#Summary
Key takeaways
- Paths 11–13 are things your code does with the S3 API: multipart for one big object, pre-signed URLs for people without credentials, Batch Operations for millions of objects already in S3. They combine with paths 1–10.
- Multipart: CreateMultipartUpload → UploadPart (in parallel, any order, ETag each) → CompleteMultipartUpload (atomic) or Abort. Parts 5 MiB–5 GiB (last smaller), 10,000 parts, 5 TB object (50 TB since Dec 2025). Recommended over 100 MB, required over 5 GB. A failed part is retried alone.
- Incomplete uploads are invisible but billed: add a lifecycle rule AbortIncompleteMultipartUpload. SSE-KMS multipart needs kms:Decrypt too.
- Pre-signed URL: one object, one method, one expiry; acts as the signer, with the signer’s permissions at use time. Console 1 min–12 h; CLI default 1 h, max 7 days with IAM user keys; with role credentials it dies with the session. The CLI presigns GET only; PUT and POST need an SDK.
- Browser → backend signs → browser PUTs straight to S3 (CORS on the bucket) → event. Pre-signed POST adds size, type and key-prefix conditions.
- Batch Operations job = manifest (Inventory, CSV or generated) + one operation + IAM role + report + priority. Operations: copy, Lambda, tags, ACL, restore, Object Lock, replicate, checksums, update encryption. Retries, partial failures in the report, job fails if >50% fail after 1,000 tasks. ~$0.25/job + $1 per million objects.
- Choosing among 13: start from where the data is, then private or not, how much, how often, and who speaks which protocol.
Answer on your own first, then read the explanation, even when you got it right.
Self-checkQuestion
Above what size must you use multipart upload, because a single PUT cannot carry the object?
Self-checkQuestion
Which statement about multipart upload parts is correct?
Self-checkQuestion
When does an object uploaded with multipart become visible to readers?
Self-checkQuestion
A company must upload a 200 GiB database export with multipart. What is the smallest part size that keeps it within the limits?
Self-checkQuestion
A mobile app uploads 3 GB videos. Users on unstable connections often see uploads fail near the end and must start again. Which change helps most?
Self-checkQuestion
Storage costs on an upload bucket keep rising, yet the console shows few objects. ListMultipartUploads returns thousands of uploads started weeks ago. Which two actions fix this?
Choose 2
Self-checkQuestion
Developers upload large files with multipart to a bucket whose default encryption is SSE-KMS. Every upload fails with Access Denied at the CompleteMultipartUpload step. Their role has s3:PutObject and kms:GenerateDataKey. What is missing?
Self-checkQuestion
Whose permissions does S3 use when someone makes a request with a pre-signed URL?
Self-checkQuestion
Which two statements about creating pre-signed URLs are true?
Choose 2
Self-checkQuestion
A pre-signed PUT URL was generated with an expiry of 5 minutes. The user starts the upload 20 minutes later. What happens?
Self-checkQuestion
A media site wants to let only paying subscribers download premium videos from a private S3 bucket, for a limited time, without creating AWS identities for them. What should the architect use?
Self-checkQuestion
An e-commerce site’s web servers struggle during sales because sellers upload many large photos through them. The bucket must stay private. Which design reduces the load most?
Self-checkQuestion
A team needs browser uploads that are rejected by S3 if the file is larger than 10 MB or is not an image. Which option enforces this?
Self-checkQuestion
Which two statements about pre-signed URL lifetimes are correct?
Choose 2
Self-checkQuestion
An account has S3 Block Public Access turned on at the account level. Will pre-signed URLs for objects in its private buckets still work?
Self-checkQuestion
A company must add a cost-centre tag to 800 million existing objects in one bucket, with retries and a record of any object that could not be tagged. What is the most operationally efficient solution?
Self-checkQuestion
What must you give S3 Batch Operations when you create a job? Choose two.
Choose 2
Self-checkQuestion
A Batch Operations Copy job finishes with status Complete, but 2% of the tasks failed: some keys were deleted after the inventory was taken, and some objects are larger than 5 GB. What should the team do next?
Self-checkQuestion
Shortly after starting, a Batch Operations job moves to Failed. More than half of its first few thousand tasks returned Access Denied. What is the most likely cause?
Self-checkQuestion
An auditor needs 2 million objects from Glacier Deep Archive restored, at the lowest cost, and is happy to wait two days. What is the right approach?
Self-checkQuestion
Which statement correctly compares S3 Batch Operations with S3 Lifecycle rules?
Self-checkQuestion
A company enabled Cross-Region Replication on a bucket that already holds 50 million objects. Only new uploads are replicated. How should the existing objects be replicated?
Self-checkQuestion
EC2 instances in a private subnet with no NAT gateway must upload large log files to an S3 bucket in the same Region, without the traffic traversing the internet and at the lowest cost. Which two should the architect use?
Choose 2
Self-checkQuestion
A data centre has 600 TB to move to S3 once. Its internet link is 500 Mbps and already busy during the day. The migration must finish within a month. What should the architect choose?
Self-checkQuestion
Business partners send invoices every night with their existing SFTP clients and cannot change them. The files must land in S3. What is the simplest solution?
Self-checkQuestion
On-premises servers must reach S3 privately over an existing Direct Connect connection, using private IP addresses. Which combination works?
Self-checkQuestion
An on-premises application writes files to an NFS share. The company wants the files stored durably in S3 while the application keeps using NFS, with recently used files served from a local cache. What fits?
Self-checkQuestion
Users across Asia, Europe and America upload large files to a single bucket in us-east-1, and uploads from Asia are slow. Which two features together improve this?
Choose 2
Self-checkQuestion
Three hundred VPCs in many accounts must reach S3 privately through a single, centrally managed set of endpoints. What is the recommended design?
Self-checkQuestion
Match the scenario to the path: a company wants to migrate 40 TB of file-server data to S3 over its 10 Gbps Direct Connect link during one weekend, keeping timestamps and verifying the copy. What should it use?