diff --git a/spring-cloud-aws-s3-parent/spring-cloud-aws-s3/src/main/java/io/awspring/cloud/s3/S3Operations.java b/spring-cloud-aws-s3-parent/spring-cloud-aws-s3/src/main/java/io/awspring/cloud/s3/S3Operations.java index b8ade4277..805f3c1ee 100644 --- a/spring-cloud-aws-s3-parent/spring-cloud-aws-s3/src/main/java/io/awspring/cloud/s3/S3Operations.java +++ b/spring-cloud-aws-s3-parent/spring-cloud-aws-s3/src/main/java/io/awspring/cloud/s3/S3Operations.java @@ -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; @@ -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 listObjects(String bucketName, String prefix); + /** * Stores a Java object in a S3 bucket. Uses {@link S3ObjectConverter} for serialization. * diff --git a/spring-cloud-aws-s3-parent/spring-cloud-aws-s3/src/main/java/io/awspring/cloud/s3/S3Template.java b/spring-cloud-aws-s3-parent/spring-cloud-aws-s3/src/main/java/io/awspring/cloud/s3/S3Template.java index 2b3fa5e02..0d55a2618 100644 --- a/spring-cloud-aws-s3-parent/spring-cloud-aws-s3/src/main/java/io/awspring/cloud/s3/S3Template.java +++ b/spring-cloud-aws-s3-parent/spring-cloud-aws-s3/src/main/java/io/awspring/cloud/s3/S3Template.java @@ -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; @@ -84,7 +89,19 @@ public void deleteObject(String s3Url) { Location location = Location.of(s3Url); this.deleteObject(location.getBucket(), location.getObject()); } - + + @Override + public List 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"); diff --git a/spring-cloud-aws-s3-parent/spring-cloud-aws-s3/src/test/java/io/awspring/cloud/s3/S3TemplateIntegrationTests.java b/spring-cloud-aws-s3-parent/spring-cloud-aws-s3/src/test/java/io/awspring/cloud/s3/S3TemplateIntegrationTests.java index 4f57d7ab5..c7310ae52 100644 --- a/spring-cloud-aws-s3-parent/spring-cloud-aws-s3/src/test/java/io/awspring/cloud/s3/S3TemplateIntegrationTests.java +++ b/spring-cloud-aws-s3-parent/spring-cloud-aws-s3/src/test/java/io/awspring/cloud/s3/S3TemplateIntegrationTests.java @@ -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; @@ -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 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"));