How to write a file or data to an S3 object using boto3
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Writing to Amazon S3 with boto3 usually means one of three things: uploading a file from disk, uploading a file-like object, or sending bytes that were generated in memory. Boto3 supports all three, and the best API depends on where the data comes from and how much control you need over metadata and transfer behavior.
Create an S3 Client the Normal Way
Most upload code starts with an S3 client:
In production, do not hardcode credentials in the script. Let boto3 resolve them from:
- an IAM role on EC2, ECS, or Lambda
- environment variables
- AWS CLI credentials
- a named profile
Using roles is usually the safest option because the code stays free of long-lived secrets.
Upload a Real File from Disk
If the content already exists as a local file, upload_file is the simplest high-level method.
This is the right default for local files because boto3 handles multipart transfer behavior for you when needed. It is usually better than reading the file yourself and pushing the bytes through put_object.
Upload Generated Data from Memory
If your program has already built the content in memory, put_object is often the most direct choice.
The Body value can be bytes or a file-like stream. Setting ContentType is a good habit because downstream tools and browsers use that metadata to interpret the object correctly.
Upload a File-Like Object
For in-memory buffers, zipped output, or generated content that behaves like a file, use upload_fileobj.
The call to seek(0) matters. If you leave the pointer at the end of the stream, boto3 uploads zero bytes.
Add Metadata and Storage Options
Uploads often need more than raw bytes. You may want metadata, cache headers, or encryption settings. With client uploads, that usually goes through ExtraArgs or direct put_object parameters.
For small in-memory writes, the equivalent put_object call is often more explicit:
Choose the form that matches how the data is already represented in your program.
Handle Errors Explicitly
S3 writes can fail for reasons that matter operationally: missing permissions, wrong bucket names, invalid regions, or network issues. Catch boto3 exceptions explicitly so you keep the AWS error details.
That is much more useful than catching a generic exception and losing the real reason the upload failed.
Verify Important Uploads
If the uploaded object matters to a workflow, verify it instead of assuming success means the right key and metadata were written.
That check catches mistakes such as:
- wrong key names
- missing content type
- empty uploads from an unrewound stream
For business-critical pipelines, validating the result is often worth the extra request.
Choosing the Right Method
A practical rule is:
- '
upload_filefor an existing local file' - '
upload_fileobjfor a readable binary stream' - '
put_objectfor smaller in-memory payloads and explicit object metadata'
That keeps the code aligned with the actual data source instead of forcing everything through one API.
Common Pitfalls
- Hardcoding AWS keys in source code instead of using IAM roles or external credential configuration.
- Using
put_objectfor large local files whenupload_fileis the more suitable upload primitive. - Forgetting
seek(0)before uploading aBytesIOobject. - Writing text without encoding it intentionally or without setting
ContentType. - Swallowing boto3 exceptions and losing the specific AWS error code that explains the failure.
Summary
- Use
upload_filefor files on disk,upload_fileobjfor streams, andput_objectfor direct in-memory writes. - Let boto3 obtain credentials from roles, profiles, or environment configuration instead of hardcoding them.
- Add metadata such as
ContentTypeand encryption settings deliberately. - Handle AWS and boto3 exceptions explicitly so failures are diagnosable.
- Verify important uploads with
head_objectwhen correctness matters.

