Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use AWS SDK for Ruby v3 and upload the completed PDF either by path with upload_file or as an open binary stream with Object#put. The path-based method is the simplest default when your PDF generator has already written a file. The stream method is useful when you need to set headers explicitly or already have an open File or Tempfile.

This guide starts after PDF generation, so it does not require a particular PDF library. It covers credentials, object keys, metadata, temporary files, multipart behavior, security, error handling and verification.

What you need before uploading

  • Ruby and the AWS SDK for Ruby v3, installed with the aws-sdk-s3 gem.
  • An S3 bucket in an AWS account, plus credentials for an IAM identity allowed to write objects to the required prefix.
  • A generated PDF available as a filesystem path, File or Tempfile.
  • A collision-safe object key, such as reports/2026/09/statement-8f31.pdf.

Install the SDK with:

gem install aws-sdk-s3

In an application, add gem "aws-sdk-s3" to the Gemfile and run bundle install. Configure AWS credentials through the standard SDK credential chain (for example, an attached role, environment variables, shared credentials file or an external identity provider). Never put access keys directly in source code.

Upload a generated PDF by file path

Aws::S3::Object#upload_file accepts a path and is the clearest option when your PDF has been saved to disk.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
require "aws-sdk-s3"

bucket = "your-bucket"
key = "reports/generated.pdf"
source_path = "/path/to/generated.pdf"

s3_object = Aws::S3::Object.new(bucket, key)
s3_object.upload_file(
  source_path,
  content_type: "application/pdf"
)

puts "Uploaded s3://#{bucket}/#{key}"

The key is the object’s name and logical path inside the bucket; S3 does not require the folders to exist first. Choose a unique key when multiple PDFs must coexist. A UUID, database identifier or date partition can prevent accidental replacement.

Setting content_type: "application/pdf" tells clients what they are downloading. Do not assume that every application-level header will be inferred automatically.

Using an S3 resource client explicitly

You can create a resource once and reuse it in a service object:

require "aws-sdk-s3"

s3 = Aws::S3::Resource.new(region: "us-east-1")
object = s3.bucket("your-bucket").object("reports/generated.pdf")
object.upload_file("/path/to/generated.pdf", content_type: "application/pdf")

Usually the SDK obtains the region from your AWS configuration. Pass a region explicitly when your deployment needs deterministic configuration.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Upload with Object#put and a binary file body

Opening the source in binary mode makes the body and its lifetime explicit. The block closes the file after the request finishes.

require "aws-sdk-s3"

object = Aws::S3::Object.new("your-bucket", "reports/generated.pdf")

File.open("/path/to/generated.pdf", "rb") do |file|
  object.put(
    body: file,
    content_type: "application/pdf"
  )
end

puts "Upload complete"

Use this form when your code already has an IO source, when you need put-specific options, or when you want resource ownership to be obvious. The caller opens and closes the file; do not close it before the request has consumed the body.

Adding server-side encryption

The S3 Ruby APIs expose server-side encryption options. Select the option that matches your bucket policy and organization’s security design. For S3-managed encryption:

object.put(
  body: File.open("/path/to/generated.pdf", "rb"),
  content_type: "application/pdf",
  server_side_encryption: "AES256"
)

Prefer a block so the file is always closed:

File.open("/path/to/generated.pdf", "rb") do |file|
  object.put(
    body: file,
    content_type: "application/pdf",
    server_side_encryption: "AES256"
  )
end

If your bucket requires a KMS key, configure the corresponding KMS encryption parameters and IAM permissions instead of copying this example unchanged. The correct key identifier and policy depend on your account.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Uploading a Ruby Tempfile

The v3 object API accepts Tempfile sources. Rewind a tempfile if your PDF generator has written to it or another operation has read from it; otherwise the upload can begin at the current cursor position rather than byte zero.

require "aws-sdk-s3"
require "tempfile"

pdf = Tempfile.new(["generated", ".pdf"])
begin
  # Replace this with your PDF generator.
  pdf.binmode
  pdf.write(pdf_bytes)
  pdf.flush
  pdf.rewind

  object = Aws::S3::Object.new("your-bucket", "reports/generated.pdf")
  object.upload_file(pdf, content_type: "application/pdf")
ensure
  pdf.close
  pdf.unlink
end

If the generator has already closed the tempfile, pass its path instead, while the file still exists:

object.upload_file(pdf.path, content_type: "application/pdf")

When you pass an open tempfile, your code remains responsible for closing it. Keep it alive until the upload returns, including when an exception is raised.

Choosing between upload_file and put

Situation Recommended call Resource responsibility Notes
PDF is complete on disk upload_file(path) SDK reads the supplied source; your code manages any separately opened resources Shortest and clearest path-based workflow
Already-open File or Tempfile upload_file(io) or put(body: io) Your code must close an open source Rewind before uploading when the cursor is not at the beginning
Need explicit request metadata or encryption options put(body: ..., content_type: ..., ...) Your code controls the IO lifetime Set application headers intentionally

Do not claim that one method is universally faster. Transfer behavior depends on the SDK abstraction, file size, network and configuration.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Large PDFs and multipart uploads

The AWS SDK for Ruby v3 Aws::S3::Object#upload_file documentation lists a default multipart threshold of 104,857,600 bytes (100 MiB). At or above that threshold, the object API uses multipart upload APIs by default. This is a version-specific, configurable SDK setting, not a universal S3 limit. Check the current API documentation and your installed SDK version before relying on the exact threshold.

The v3 TransferManager also documents multipart behavior and parallel part uploads. For very large generated PDFs, consider the transfer abstraction and its configuration deliberately. Multipart uploads improve recoverability for large transfers, but they still require sufficient IAM permissions and cleanup of failed multipart sessions according to your operational policy.

Credentials, IAM and object privacy

The uploading identity needs permission to write the target object, normally s3:PutObject on the relevant bucket and key prefix. Additional permissions may be needed for KMS encryption, tags or other bucket controls. The exact policy is account-specific, so grant only the actions and resources your application requires.

Do not make a PDF public merely to make it downloadable. Keep the bucket and object private unless public access is an explicit requirement. Serve authorized files through your application or issue a presigned URL after checking the requesting user’s permissions. Bucket policies, Block Public Access settings, object ownership and encryption requirements can override assumptions made in sample code.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Confirming success and handling failures

An SDK call that returns without raising an exception indicates that the request completed from the client’s perspective. You can inspect the object with head when your workflow needs an explicit check:

head = object.head
puts head.content_length
puts head.content_type

Wrap uploads in a rescue branch that records the key and request context without logging secrets:

begin
  File.open(source_path, "rb") do |file|
    object.put(body: file, content_type: "application/pdf")
  end
rescue Aws::S3::Errors::ServiceError => e
  warn "S3 upload failed for #{object.key}: #{e.class}: #{e.message}"
  raise
end

Retry transient failures using the SDK’s configured retry behavior or an application-level policy. Avoid blindly retrying a non-idempotent workflow under a new key; decide whether replacing the same key is safe for your application.

Common errors and fixes

  • AccessDenied: verify the runtime identity, bucket name, key prefix, bucket policy, KMS permissions and any organization-level restrictions.
  • NoSuchBucket: check spelling, account and region. A bucket name can exist in a different account than the credentials being used.
  • ExpiredToken or invalid credentials: refresh the role or environment credentials and confirm the process sees the intended AWS profile.
  • Empty or truncated PDF: flush and rewind a Tempfile before passing it; ensure the generator finished writing before upload.
  • Wrong download behavior: set content_type: "application/pdf" and, if needed, configure disposition metadata according to your serving requirements.
  • Object unexpectedly replaced: two jobs used the same key. Add a unique identifier, enable bucket versioning where appropriate, or coordinate writes at the application layer.
  • Large upload interruption: use the SDK’s multipart-capable transfer path, verify network timeouts and permissions, and monitor incomplete multipart uploads.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Operational checklist

  1. Generate the PDF completely and verify its path or IO source.
  2. Choose a stable, collision-safe S3 key.
  3. Use an IAM role or other managed credential source, never hard-coded secrets.
  4. Upload with upload_file for a completed path, or put for an explicitly managed binary stream.
  5. Set content_type: "application/pdf"; add encryption options required by your bucket.
  6. Close and unlink temporary files after the request, even on failure.
  7. Record the bucket, key and outcome; do not log credentials or sensitive PDF contents.
  8. Verify with head or an authorized retrieval test when your workflow requires confirmation.

Or skip the browser setup

If you also need a clean screenshot or PDF capture of a web page for a report pipeline, ScreenshotNeo provides a single HTTP request rather than a browser setup. It accepts cookie and consent banners, removes more than 60 known consent platforms plus newsletter popups and chat widgets before capture, and bills only clean shots: bot checks, blank pages, failed loads, timeouts and cache hits are not billed. Its MCP server provides take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Example cURL request (see the ScreenshotNeo API documentation):

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

ScreenshotNeo includes 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

Frequently Asked Questions

Can I upload a PDF directly from memory instead of creating a file?

Yes. Pass an IO-like object containing the PDF bytes as the body to Object#put, and ensure the stream is positioned at the beginning. A file or tempfile is often easier to manage for large output.

Does S3 automatically set the PDF content type?

Set content_type: "application/pdf" explicitly so downloads and browsers receive the intended metadata.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Will uploading the same key preserve the previous PDF?

Normally a write targets that key and can replace the existing object. Use unique keys, bucket versioning or application-level coordination when every revision must be retained.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.