java

AWS SDK for Java v2

The JVM client — Java, Kotlin and Scala

Install

XML
<dependency>
  <groupId>software.amazon.awssdk</groupId>
  <artifactId>s3</artifactId>
  <version>2.29.0</version>
</dependency>

Configure

Configure the endpoint, region, credential and bucket in this tool’s vocabulary. Path-style addressing is recommended for dotted bucket names and clients without nested wildcard TLS, but virtual-hosted addressing is also supported.

In AWS SDK for Java v2Set it to
endpointOverridehttps://s3.canada.popcloud.ca
regioncanada
AwsBasicCredentials.createPCAK00EXAMPLEKEYID00
AwsBasicCredentials.create<your secret access key>
forcePathStyletrue
Environment
example values
export AWS_ACCESS_KEY_ID=PCAK00EXAMPLEKEYID00
export AWS_SECRET_ACCESS_KEY=<your secret access key>
export AWS_ENDPOINT_URL=https://s3.canada.popcloud.ca
export AWS_REGION=canada
export POPCLOUD_BUCKET=pc-your-org-media

These are example values. Sign in and every snippet on this site fills in with your own endpoint, key and bucket.

Java
static S3Client newClient() {
    return S3Client.builder()
            .endpointOverride(URI.create(System.getenv("AWS_ENDPOINT_URL"))) // https://s3.<region>.popcloud.ca
            .region(Region.of(System.getenv("AWS_REGION")))                  // canada
            .credentialsProvider(StaticCredentialsProvider.create(
                    AwsBasicCredentials.create(
                            System.getenv("AWS_ACCESS_KEY_ID"),
                            System.getenv("AWS_SECRET_ACCESS_KEY"))))
            // Recommended for predictable custom-endpoint behavior. Virtual-hosted
            // requests are supported too for ordinary single-label bucket names.
            .forcePathStyle(true)
            .build();
}

Use it

Upload an object

Java
static void upload(S3Client s3, String bucket, String key, byte[] body) {
    s3.putObject(
            PutObjectRequest.builder()
                    .bucket(bucket)
                    .key(key)
                    .contentType("text/plain")
                    .build(),
            RequestBody.fromBytes(body));
}

Download an object

Java
static byte[] download(S3Client s3, String bucket, String key) {
    return s3.getObjectAsBytes(
                    GetObjectRequest.builder().bucket(bucket).key(key).build())
            .asByteArray();
}

List a prefix

Java
static List<String> listPrefix(S3Client s3, String bucket, String prefix) {
    List<String> keys = new ArrayList<>();
    s3.listObjectsV2Paginator(
                    ListObjectsV2Request.builder().bucket(bucket).prefix(prefix).build())
            .contents()
            .forEach((S3Object object) -> keys.add(object.key()));
    return keys;
}

Presign a URL

Java
static URL presign(String bucket, String key) {
    try (S3Presigner presigner = S3Presigner.builder()
            .endpointOverride(URI.create(System.getenv("AWS_ENDPOINT_URL")))
            .region(Region.of(System.getenv("AWS_REGION")))
            .credentialsProvider(StaticCredentialsProvider.create(
                    AwsBasicCredentials.create(
                            System.getenv("AWS_ACCESS_KEY_ID"),
                            System.getenv("AWS_SECRET_ACCESS_KEY"))))
            // The presigner needs path style told to it separately from the client.
            .serviceConfiguration(software.amazon.awssdk.services.s3.S3Configuration.builder()
                    .pathStyleAccessEnabled(true)
                    .build())
            .build()) {

        return presigner.presignGetObject(GetObjectPresignRequest.builder()
                        .signatureDuration(Duration.ofHours(1)) // 7 days is the maximum
                        .getObjectRequest(GetObjectRequest.builder()
                                .bucket(bucket)
                                .key(key)
                                .build())
                        .build())
                .url();
    }
}

Delete an object

Java
static void delete(S3Client s3, String bucket, String key) {
    s3.deleteObject(DeleteObjectRequest.builder().bucket(bucket).key(key).build());
}

What to watch for

These apply to AWS SDK for Java v2 specifically. The full list covers the platform.

You’ll see
501 NotImplemented on an upload whose x-amz-content-sha256 is STREAMING-AWS4-HMAC-SHA256-PAYLOAD.
Why
That encoding signs every chunk of the body separately, which a re-signing proxy cannot forward without buffering the whole object. The platform streams instead, and never holds a body in memory.
Instead
Nothing to do on a current SDK — the modern default is STREAMING-UNSIGNED-PAYLOAD-TRAILER, which works, checksum trailer and all. Only older SDK versions and explicitly-configured chunked signing hit this. If you do, set the payload signing option off (`AWS_S3_DISABLE_CHUNKED_ENCODING`, `chunkedEncodingEnabled=false`, or the equivalent) and the SDK falls back to a supported encoding.
You’ll see
405 MethodNotAllowed from CreateBucket or DeleteBucket — `aws s3 mb`, `mc mb`, or a tool provisioning its own bucket on first run.
Why
Bucket lifecycle belongs to the control plane, which also allocates the storage account, the data-plane key and the CORS rules that come with it. Letting the edge create buckets would create half of one.
Instead
Create buckets in the dashboard, or with `POST /v1/buckets` on the control-plane API. Everything object-level then works normally against that bucket.

How this page is kept true

Every snippet above was extracted from examples/java/aws-sdk-java-v2, a program that uploads, downloads, compares bytes, lists, presigns and cleans up after itself. It runs in CI against a live sandbox organisation via make docs-verify-java. If it stops passing, this page is wrong and we treat that as a bug in the product.

A few snippets on this page are marked not run in CI — they need a browser, a second provider, or a configuration change the sandbox cannot make. Those are reviewed by hand.

Not yet run against the live sandbox — the runner is wired, the first verified run stamps this line.