Warning
This article was automatically translated by OpenAI (gpt-4o).It may be edited eventually, but please be aware that it may contain incorrect information at this time.
When accessing S3 with Java,
are potential candidates. While they are well-maintained and feature-rich, these libraries have a lot of dependencies.
In the case of AWS SDK for Java 2.0,
\- software.amazon.awssdk:s3:jar:2.26.16:compile
+- software.amazon.awssdk:aws-xml-protocol:jar:2.26.16:compile
| \- software.amazon.awssdk:aws-query-protocol:jar:2.26.16:compile
+- software.amazon.awssdk:protocol-core:jar:2.26.16:compile
+- software.amazon.awssdk:arns:jar:2.26.16:compile
+- software.amazon.awssdk:profiles:jar:2.26.16:compile
+- software.amazon.awssdk:crt-core:jar:2.26.16:compile
+- software.amazon.awssdk:http-auth:jar:2.26.16:compile
+- software.amazon.awssdk:identity-spi:jar:2.26.16:compile
+- software.amazon.awssdk:http-auth-spi:jar:2.26.16:compile
| \- org.reactivestreams:reactive-streams:jar:1.0.4:compile
+- software.amazon.awssdk:http-auth-aws:jar:2.26.16:compile
+- software.amazon.awssdk:checksums:jar:2.26.16:compile
+- software.amazon.awssdk:checksums-spi:jar:2.26.16:compile
+- software.amazon.awssdk:retries-spi:jar:2.26.16:compile
+- software.amazon.awssdk:sdk-core:jar:2.26.16:compile
| +- software.amazon.awssdk:retries:jar:2.26.16:compile
| \- org.slf4j:slf4j-api:jar:2.0.13:compile
+- software.amazon.awssdk:auth:jar:2.26.16:compile
| \- software.amazon.eventstream:eventstream:jar:1.0.1:compile
+- software.amazon.awssdk:http-client-spi:jar:2.26.16:compile
+- software.amazon.awssdk:regions:jar:2.26.16:compile
+- software.amazon.awssdk:annotations:jar:2.26.16:compile
+- software.amazon.awssdk:utils:jar:2.26.16:compile
+- software.amazon.awssdk:aws-core:jar:2.26.16:compile
+- software.amazon.awssdk:metrics-spi:jar:2.26.16:compile
+- software.amazon.awssdk:json-utils:jar:2.26.16:compile
| \- software.amazon.awssdk:third-party-jackson-core:jar:2.26.16:compile
+- software.amazon.awssdk:endpoints-spi:jar:2.26.16:compile
+- software.amazon.awssdk:apache-client:jar:2.26.16:runtime
| +- org.apache.httpcomponents:httpclient:jar:4.5.13:runtime
| | \- commons-logging:commons-logging:jar:1.2:runtime
| +- org.apache.httpcomponents:httpcore:jar:4.4.16:runtime
| \- commons-codec:commons-codec:jar:1.16.1:runtime
\- software.amazon.awssdk:netty-nio-client:jar:2.26.16:runtime
+- io.netty:netty-codec-http:jar:4.1.111.Final:runtime
+- io.netty:netty-codec-http2:jar:4.1.111.Final:runtime
+- io.netty:netty-codec:jar:4.1.111.Final:runtime
+- io.netty:netty-transport:jar:4.1.111.Final:runtime
+- io.netty:netty-common:jar:4.1.111.Final:runtime
+- io.netty:netty-buffer:jar:4.1.111.Final:runtime
+- io.netty:netty-handler:jar:4.1.111.Final:runtime
| \- io.netty:netty-transport-native-unix-common:jar:4.1.111.Final:runtime
+- io.netty:netty-transport-classes-epoll:jar:4.1.111.Final:runtime
\- io.netty:netty-resolver:jar:4.1.111.Final:runtime
In the case of MinIO Java SDK,
\- io.minio:minio:jar:8.5.11:compile
+- com.carrotsearch.thirdparty:simple-xml-safe:jar:2.7.1:compile
+- com.google.guava:guava:jar:33.0.0-jre:compile
| +- com.google.guava:failureaccess:jar:1.0.2:compile
| +- com.google.guava:listenablefuture:jar:9999.0-empty-to-avoid-conflict-with-guava:compile
| +- com.google.code.findbugs:jsr305:jar:3.0.2:compile
| +- org.checkerframework:checker-qual:jar:3.41.0:compile
| +- com.google.errorprone:error_prone_annotations:jar:2.23.0:compile
| \- com.google.j2objc:j2objc-annotations:jar:2.8:compile
+- com.squareup.okhttp3:okhttp:jar:4.12.0:compile
| +- com.squareup.okio:okio:jar:3.6.0:compile
| | \- com.squareup.okio:okio-jvm:jar:3.6.0:compile
| | \- org.jetbrains.kotlin:kotlin-stdlib-common:jar:1.9.24:compile
| \- org.jetbrains.kotlin:kotlin-stdlib-jdk8:jar:1.9.24:compile
| +- org.jetbrains.kotlin:kotlin-stdlib:jar:1.9.24:compile
| | \- org.jetbrains:annotations:jar:13.0:compile
| \- org.jetbrains.kotlin:kotlin-stdlib-jdk7:jar:1.9.24:compile
+- com.fasterxml.jackson.core:jackson-annotations:jar:2.17.1:compile
+- com.fasterxml.jackson.core:jackson-core:jar:2.17.1:compile
+- com.fasterxml.jackson.core:jackson-databind:jar:2.17.1:compile
+- org.bouncycastle:bcprov-jdk18on:jar:1.78:compile
+- org.apache.commons:commons-compress:jar:1.26.0:compile
| +- commons-io:commons-io:jar:2.15.1:compile
| \- org.apache.commons:commons-lang3:jar:3.14.0:compile
+- commons-codec:commons-codec:jar:1.16.1:compile
\- org.xerial.snappy:snappy-java:jar:1.1.10.5:compile
Note
In the case of AWS SDK for Java, which is nearing EoL, the dependencies are somewhat better.
\- com.amazonaws:aws-java-sdk-s3:jar:1.12.757:compile
+- com.amazonaws:aws-java-sdk-kms:jar:1.12.757:compile
+- com.amazonaws:aws-java-sdk-core:jar:1.12.757:compile
| +- commons-logging:commons-logging:jar:1.1.3:compile
| +- commons-codec:commons-codec:jar:1.16.1:compile
| +- org.apache.httpcomponents:httpclient:jar:4.5.13:compile
| | \- org.apache.httpcomponents:httpcore:jar:4.4.16:compile
| +- com.fasterxml.jackson.core:jackson-databind:jar:2.17.1:compile
| | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.17.1:compile
| | \- com.fasterxml.jackson.core:jackson-core:jar:2.17.1:compile
| +- com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:jar:2.17.1:compile
| \- joda-time:joda-time:jar:2.12.7:compile
\- com.amazonaws:jmespath-java:jar:1.12.757:compile
If you are using other AWS services, AWS SDK for Java 2.0 might be fine, but if you are only doing simple put/get operations on S3-compatible Object Storage, these libraries are too much. Additionally, AWS SDK uses Apache HTTP Client or Netty, and MinIO SDK uses OkHttp (implemented in Kotlin) as their HTTP clients.
When creating an application with Spring Boot, you usually use RestTemplate or RestClient. If the SDK uses a separate HTTP client, it becomes inconvenient as you cannot use the Interceptors for Observability, Logging, Retry, etc., that you have set up on the application side.
For simple use cases where you only do put/get operations with access key and secret key authentication, it is sufficient to create HTTP Headers for S3 and perform XML marshalling/unmarshalling. This can be done with just RestTemplate or RestClient without using an SDK.
Therefore, I created a library called simple-s3-client. Although it is named "s3-client," it only includes utilities for creating HTTP Headers and data classes for commonly used APIs.
You can use it by adding the following dependency:
<dependency>
<groupId>am.ik.s3</groupId>
<artifactId>simple-s3-client</artifactId>
<version>0.2.2</version>
</dependency>
Usage is as follows:
import static am.ik.s3.S3RequestBuilder.s3Request;
URI endpoint = URI.create("https://...");
String region = "...";
String accessKeyId = "...";
String secretAccessKey = "...";
String bucket = "...";
To PUT an object:
// Put an object
String body = "Hello World!";
S3Request putObjectRequest = s3Request().endpoint(endpoint)
.region(region)
.accessKeyId(accessKeyId)
.secretAccessKey(secretAccessKey)
.method(HttpMethod.PUT)
.path(b -> b.bucket(bucket).key("hello.txt"))
.content(S3Content.of(body, MediaType.TEXT_PLAIN))
.build();
In the case of RestTemplate:
restTemplate.exchange(putObjectRequest.toEntityBuilder().body(body), Void.class);
In the case of RestClient:
restClient.put()
.uri(putObjectRequest.uri())
.headers(putObjectRequest.headers())
.body(body)
.retrieve()
.toBodilessEntity();
Note
Multipart is not supported.
To GET an object:
// Get an object
S3Request getObjectRequest = s3Request().endpoint(endpoint)
.region(region)
.accessKeyId(accessKeyId)
.secretAccessKey(secretAccessKey)
.method(HttpMethod.GET)
.path(b -> b.bucket(bucket).key("hello.txt"))
.build();
In the case of RestTemplate:
String response = restTemplate.exchange(getObjectRequest.toEntityBuilder().build(), String.class).getBody();
System.out.println("Response: " + response); // Response: Hello World!
In the case of RestClient:
String response = restClient.get()
.uri(getObjectRequest.uri())
.headers(getObjectRequest.headers())
.retrieve()
.body(String.class);
System.out.println("Response: " + response); // Response: Hello World!
For other usage, please refer to the README.
The dependencies are as follows, and since it only adds jackson-dataformat-xml for handling XML with Jackson to the spring-web required for creating a web application with Spring Boot, it is minimal from the perspective of a typical Spring Boot application.
\- am.ik.s3:simple-s3-client:jar:0.2.2:compile
+- org.springframework:spring-web:jar:6.1.10:compile
| +- org.springframework:spring-beans:jar:6.1.10:compile
| +- org.springframework:spring-core:jar:6.1.10:compile
| | \- org.springframework:spring-jcl:jar:6.1.10:compile
| \- io.micrometer:micrometer-observation:jar:1.13.1:compile
| \- io.micrometer:micrometer-commons:jar:1.13.1:compile
+- com.fasterxml.jackson.dataformat:jackson-dataformat-xml:jar:2.17.1:compile
| +- com.fasterxml.jackson.core:jackson-core:jar:2.17.1:compile
| +- com.fasterxml.jackson.core:jackson-annotations:jar:2.17.1:compile
| +- com.fasterxml.jackson.core:jackson-databind:jar:2.17.1:compile
| \- org.codehaus.woodstox:stax2-api:jar:4.2.2:compile
\- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.17.1:compile