Skip to content

Commit

Permalink
Add S3Client listObjects support in S3Template (#831)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexisgra committed Aug 14, 2023
1 parent 14d910c commit 6716eb7
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.io.InputStream;
import java.net.URL;
import java.time.Duration;
import java.util.List;
import org.springframework.lang.Nullable;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.CreateBucketResponse;
Expand Down Expand Up @@ -60,7 +61,17 @@ public interface S3Operations {
* @param s3Url - the S3 url s3://bucket/key
*/
void deleteObject(String s3Url);


/**
* Returns some or all (up to 1,000) of the objects in a bucket.
* Does not handle pagination. If you need pagination you should use {@link S3PathMatchingResourcePatternResolver} or {@link S3Client}
*
* @param bucketName - the bucket name
* @param prefix - objects prefix
* @return list of {@link S3Resource}
*/
List<S3Resource> listObjects(String bucketName, String prefix);

/**
* Stores a Java object in a S3 bucket. Uses {@link S3ObjectConverter} for serialization.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,16 @@
import java.io.OutputStream;
import java.net.URL;
import java.time.Duration;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StreamUtils;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request;
import software.amazon.awssdk.services.s3.model.ListObjectsV2Response;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
Expand Down Expand Up @@ -84,7 +89,19 @@ public void deleteObject(String s3Url) {
Location location = Location.of(s3Url);
this.deleteObject(location.getBucket(), location.getObject());
}


@Override
public List<S3Resource> listObjects(String bucketName, String prefix) {
Assert.notNull(bucketName, "bucketName is required");
Assert.notNull(prefix, "prefix is required");

final ListObjectsV2Request request = ListObjectsV2Request.builder().bucket(bucketName).prefix(prefix).build();
final ListObjectsV2Response response = s3Client.listObjectsV2(request);

return response.contents().stream()
.map(s3Object -> new S3Resource(bucketName, s3Object.key(), s3Client, s3OutputStreamProvider)).toList();
}

@Override
public S3Resource store(String bucketName, String key, Object object) {
Assert.notNull(bucketName, "bucketName is required");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
Expand Down Expand Up @@ -147,6 +149,20 @@ void deletesObjectByS3Url() {
.isThrownBy(() -> client.headObject(r -> r.bucket(BUCKET_NAME).key("key.txt")));
}

@Test
void listObjects() throws IOException {
client.putObject(r -> r.bucket(BUCKET_NAME).key("hello-en.txt"), RequestBody.fromString("hello"));
client.putObject(r -> r.bucket(BUCKET_NAME).key("hello-fr.txt"), RequestBody.fromString("bonjour"));
client.putObject(r -> r.bucket(BUCKET_NAME).key("bye.txt"), RequestBody.fromString("bye"));

List<S3Resource> resources = s3Template.listObjects(BUCKET_NAME, "hello");
assertThat(resources.size()).isEqualTo(2);

// According to the S3Client doc : "Objects are returned sorted in an ascending order of the respective key names in the list."
assertThat(resources).extracting(S3Resource::getInputStream).map(is -> new String(is.readAllBytes(), StandardCharsets.UTF_8))
.containsExactly("hello", "bonjour");
}

@Test
void storesObject() throws IOException {
S3Resource storedObject = s3Template.store(BUCKET_NAME, "person.json", new Person("John", "Doe"));
Expand Down

0 comments on commit 6716eb7

Please sign in to comment.