Free tools Windows power users keep installed
One-click scans. No signup required.
Generate the PDF first, then upload the resulting bytes, file, or stream to S3. With the AWS SDK for Java 2.x, use RequestBody.fromFile for a PDF already written to disk, RequestBody.fromBytes for a byte array, or RequestBody.fromInputStream when you have a stream and its exact length. For asynchronous transfers, use AsyncRequestBody and wait for the returned completion stage before treating the object as uploaded.
What the upload step does (and does not do)
S3 stores objects; it does not create a PDF from Java objects or HTML. Your application must first call its chosen PDF library and obtain one of these outputs:
- A local
Pathor file. - A complete
byte[]in memory. - An
InputStream, preferably with a known exact byte length.
You then build a PutObjectRequest, choose a bucket and object key, set metadata such as application/pdf, and provide a matching request body. The examples below use AWS SDK for Java 2.x APIs; SDK 1.x examples use different classes and method signatures.
Choose the upload method that matches your PDF output
| PDF output | SDK 2.x body | Best fit | Main consideration |
|---|---|---|---|
| Local file | RequestBody.fromFile(path) |
The generator writes to disk | Requires temporary or permanent storage |
| Byte array | RequestBody.fromBytes(bytes) |
Small or moderate PDFs already in memory | Memory use grows with the complete document |
| InputStream with known size | RequestBody.fromInputStream(stream, length) |
Streaming APIs that expose an exact length | The length must equal the bytes that will be read |
| InputStream with unknown size | A content provider that buffers, or multipart/async design | Large or genuinely open-ended streams | Buffering can consume substantial memory; multipart handling is more involved |
Prerequisites
- An AWS account with an S3 bucket in the region you intend to use.
- Credentials available through the SDK’s normal provider chain (for example, an IAM role, environment variables, or a configured profile). Do not hard-code access keys in source.
- The AWS SDK for Java 2.x S3 module, with a version selected by your project’s dependency management.
- An IAM principal allowed to write objects to the destination bucket and key prefix.
The code deliberately does not pin a dependency version because the correct version is determined by your application’s existing SDK BOM and compatibility requirements.
Upload a generated PDF file synchronously
This complete class accepts a PDF path, bucket, key, and optional region from the command line. Your PDF-generation code runs before this class (or writes the path that it receives).
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import java.nio.file.Path;
import java.nio.file.Paths;
public final class UploadPdf {
public static void main(String[] args) {
if (args.length < 3) {
throw new IllegalArgumentException("Usage: UploadPdf <pdf-path> <bucket> <key> [region]");
}
Path pdfPath = Paths.get(args[0]);
String bucket = args[1];
String key = args[2];
Region region = args.length > 3 ? Region.of(args[3]) : Region.US_EAST_1;
PutObjectRequest request = PutObjectRequest.builder()
.bucket(bucket)
.key(key)
.contentType("application/pdf")
.build();
try (S3Client s3 = S3Client.builder().region(region).build()) {
s3.putObject(request, RequestBody.fromFile(pdfPath));
}
System.out.println("Uploaded s3://" + bucket + "/" + key);
}
}
Compile this against the SDK 2.x S3 module and run it after configuring credentials. putObject returns only after the synchronous request completes or throws an exception. Closing the client releases its resources; a long-running service should normally create a client once and reuse it rather than opening one for every PDF.
Upload bytes produced in memory
If the PDF library returns a byte[], use RequestBody.fromBytes. This avoids creating a temporary file but keeps the entire document in memory.
byte[] pdfBytes = createPdfWithYourLibrary();
PutObjectRequest request = PutObjectRequest.builder()
.bucket(bucketName)
.key(objectKey)
.contentType("application/pdf")
.build();
s3Client.putObject(request, RequestBody.fromBytes(pdfBytes));
Do not call this method with a partial buffer. The array must contain exactly the PDF bytes you want S3 to store. For large documents, a file or streaming strategy can avoid a second full in-memory copy.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesRank #2
Upload an InputStream when its length is known
For a stream, pass the exact number of bytes that will be read:
try (InputStream pdfInputStream = openGeneratedPdfStream()) {
long pdfLength = exactLengthOfTheStream();
PutObjectRequest request = PutObjectRequest.builder()
.bucket(bucketName)
.key(objectKey)
.contentType("application/pdf")
.build();
s3Client.putObject(
request,
RequestBody.fromInputStream(pdfInputStream, pdfLength));
}
Length is a correctness requirement, not an optimization. A value smaller than the actual byte count can truncate the object. A value larger than the bytes available can make the request fail or wait for data that never arrives. AWS’s stream-upload guidance is explicit: always provide the exact content length when it is available.
What to do when the stream length is unknown
A synchronous SDK operation still needs a reliable content-length strategy. A content provider that discovers the length may buffer the complete stream in memory, which can be unacceptable for large PDFs. Consider these alternatives:
Write to a temporary file first
Let the PDF generator finish into a temporary Path, obtain the file size, and use RequestBody.fromFile. Delete the file only after putObject succeeds or after your error-handling policy has preserved enough information to retry.
Use multipart upload for large content
Multipart upload is designed for objects that are large or whose final size is not available up front. It requires initiating an upload, sending parts, recording their completion information, and completing (or aborting) the upload. The synchronous SDK and AWS transfer utilities provide multipart-capable approaches; select one that matches your stream source and memory budget.
Use an asynchronous body for an asynchronous pipeline
SDK 2.x asynchronous operations use AsyncRequestBody, not the synchronous RequestBody. The body can be backed by a file or a publisher appropriate to your producer. Make sure the producer signals completion and propagates read errors; an asynchronous method call returning does not mean the object is durable in S3.
Asynchronous file upload and completion handling
When the PDF is already on disk and your application should not block its calling thread, use S3AsyncClient. The returned future must be observed:
import software.amazon.awssdk.core.async.AsyncRequestBody;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import java.nio.file.Path;
import java.util.concurrent.CompletableFuture;
S3AsyncClient s3 = S3AsyncClient.create();
PutObjectRequest request = PutObjectRequest.builder()
.bucket(bucketName)
.key(objectKey)
.contentType("application/pdf")
.build();
CompletableFuture<PutObjectResponse> upload = s3.putObject(
request, AsyncRequestBody.fromFile(pdfPath));
upload.whenComplete((response, error) -> {
if (error != null) {
logUploadFailure(error);
} else {
markUploadComplete(response);
}
});
// If the current operation must not continue until S3 confirms completion:
upload.join();
s3.close();
In a server, keep the client alive for the service lifetime and close it during shutdown. In a command-line process, waiting on the future before exit prevents the JVM from ending while the transfer is still in progress. AWS also documents S3 Transfer Manager for file uploads; its completion future should be awaited when subsequent work depends on a finished upload.
Rank #4
Set object metadata and choose a stable key
Set contentType("application/pdf") so consumers receive the intended media type. Build keys from your application’s identity and retention rules, such as invoices/2026/09/invoice-1234.pdf, rather than from an untrusted filename. S3 keys are not local paths: do not assume that creating a key creates directories, and avoid putting secrets or personal data in a key that appears in logs and URLs.
If an existing key is uploaded again, the new object replaces the previous current version unless your bucket’s versioning policy changes that behavior. If replacement must be prevented, generate unique keys or add an application-level existence check and conflict policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Reliability, throughput, and cost decisions
Memory
fromBytes is simple, but the complete PDF remains in memory. A file-backed upload shifts storage to disk. Unknown-length synchronous streaming can buffer the entire stream, so estimate the worst-case document size before choosing it.
Latency and concurrency
A synchronous call ties up the calling thread until S3 responds. Asynchronous clients let your application overlap PDF generation and network work, but you still need bounded concurrency so producers do not create more data than the upload path can consume.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallBest Value
Completion and retries
Record success only after the synchronous call returns or the asynchronous future completes without an error. On failure, retain the source (file, bytes, or a reproducible generation request) so your retry logic does not attempt to upload an already-consumed stream. Multipart workflows also need explicit abort handling for incomplete uploads.
Billing
S3 charges depend on the storage class, requests, transfer, and region selected for your AWS account. The SDK upload method does not determine a universal price; consult your account’s current S3 pricing and lifecycle configuration when estimating cost.
Troubleshooting common failures
| Symptom | Likely cause | Fix |
|---|---|---|
| Access denied | The credentials lack permission for the bucket/key, or a bucket policy blocks the request. | Check the active credentials, bucket name, region, and the IAM action that permits object writes to that key prefix. |
| Uploaded PDF is truncated | The declared stream length was smaller than the bytes produced. | Measure the exact byte count or use a file/byte-array method whose length is known by the SDK. |
| Request hangs or fails near the end | The declared stream length is larger than the bytes available. | Fix the length calculation; do not guess. For unknown large streams, use a multipart or asynchronous design. |
NoSuchKey when downloading |
The download key differs from the upload key, including case or prefix. | Log the complete s3://bucket/key identifier and use that exact key for retrieval. |
| Application exits before an async upload finishes | The completion future was ignored. | Await it with join/get where completion is required, or keep the service alive and handle the callback. |
| PDF downloads with the wrong media type | No content type was supplied, or metadata was set incorrectly. | Set contentType("application/pdf") on PutObjectRequest and verify the stored object’s metadata. |
| SDK compilation errors around request bodies | SDK 1.x and 2.x snippets were mixed. | Use software.amazon.awssdk 2.x imports consistently; do not combine them with v1 request-body classes. |
Or skip the browser setup
If your pipeline also needs clean screenshots of web pages—for example, to archive a rendered invoice preview—ScreenshotNeo can provide the capture step without maintaining browser automation. One GET request returns an image or PDF:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for all request options. Before capture it accepts cookie/consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan. Create a free ScreenshotNeo account.
Frequently Asked Questions
Should I close an S3 client after every PDF?
No. Reuse a client in a long-running service and close it during application shutdown. A short-lived command-line process can use try-with-resources.
Can the upload call generate the PDF for me?
No. PDF generation remains a separate application step; S3 receives the file, bytes, or stream that your Java code supplies.
Quick Recap
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.

