Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,12 @@
<properties>
<maven.compiler.release>8</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jackson.version>2.14.2</jackson.version>
<aws.version>1.12.477</aws.version>
<netcdfJavaVersion>5.5.3</netcdfJavaVersion>
<zstdVersion>1.5.5-5</zstdVersion>
<junit-jupiter-version>5.10.2</junit-jupiter-version>
<jackson.version>2.20.0</jackson.version>
<aws.version>2.34.6</aws.version>
<netcdfJavaVersion>5.9.1</netcdfJavaVersion>
<zstdVersion>1.5.5-7</zstdVersion>
<junit-jupiter-version>5.14.0</junit-jupiter-version>
<findbugs.version>3.0.2</findbugs.version>
</properties>

<dependencies>
Expand Down Expand Up @@ -83,14 +84,19 @@
<artifactId>jackson-datatype-jdk8</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>${findbugs.version}</version>
</dependency>
<dependency>
<groupId>edu.ucar</groupId>
<artifactId>cdm-core</artifactId>
<version>${netcdfJavaVersion}</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>${aws.version}</version>
</dependency>
<dependency>
Expand Down
57 changes: 40 additions & 17 deletions src/main/java/dev/zarr/zarrjava/store/S3Store.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package dev.zarr.zarrjava.store;

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.S3ObjectInputStream;
import dev.zarr.zarrjava.utils.Utils;
import software.amazon.awssdk.core.ResponseInputStream;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import software.amazon.awssdk.services.s3.model.HeadObjectRequest;
import software.amazon.awssdk.services.s3.model.ListObjectsRequest;
import software.amazon.awssdk.services.s3.model.ListObjectsResponse;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
Expand All @@ -16,13 +23,13 @@
public class S3Store implements Store, Store.ListableStore {

@Nonnull
private final AmazonS3 s3client;
private final S3Client s3client;
@Nonnull
private final String bucketName;
@Nullable
private final String prefix;

public S3Store(@Nonnull AmazonS3 s3client, @Nonnull String bucketName, @Nullable String prefix) {
public S3Store(@Nonnull S3Client s3client, @Nonnull String bucketName, @Nullable String prefix) {
this.s3client = s3client;
this.bucketName = bucketName;
this.prefix = prefix;
Expand All @@ -40,8 +47,7 @@ String resolveKeys(String[] keys) {

@Nullable
ByteBuffer get(GetObjectRequest getObjectRequest) {
try (S3ObjectInputStream inputStream = s3client.getObject(getObjectRequest)
.getObjectContent()) {
try (ResponseInputStream<GetObjectResponse> inputStream = s3client.getObject(getObjectRequest)) {
return Utils.asByteBuffer(inputStream);
} catch (IOException e) {
return null;
Expand All @@ -50,48 +56,65 @@ ByteBuffer get(GetObjectRequest getObjectRequest) {

@Override
public boolean exists(String[] keys) {
return s3client.doesObjectExist(bucketName, resolveKeys(keys));
HeadObjectRequest req = HeadObjectRequest.builder().bucket(bucketName).key(resolveKeys(keys)).build();
return s3client.headObject(req).sdkHttpResponse().statusCode() == 200;
}

@Nullable
@Override
public ByteBuffer get(String[] keys) {
return get(new GetObjectRequest(bucketName, resolveKeys(keys)));
return get(GetObjectRequest.builder().bucket(bucketName).key(resolveKeys(keys))
.build());
}

@Nullable
@Override
public ByteBuffer get(String[] keys, long start) {
return get(new GetObjectRequest(bucketName, resolveKeys(keys)).withRange(start));
GetObjectRequest req = GetObjectRequest.builder()
.bucket(bucketName)
.key(resolveKeys(keys))
.range(String.valueOf(start))
.build();
return get(req);
}

@Nullable
@Override
public ByteBuffer get(String[] keys, long start, long end) {
return get(new GetObjectRequest(bucketName, resolveKeys(keys)).withRange(start, end));
GetObjectRequest req = GetObjectRequest.builder()
.bucket(bucketName)
.key(resolveKeys(keys))
.range(String.valueOf(start)+"-"+String.valueOf(end))
.build();
return get(req);
}

@Override
public void set(String[] keys, ByteBuffer bytes) {
try (InputStream byteStream = new ByteArrayInputStream(Utils.toArray(bytes))) {
s3client.putObject(bucketName, resolveKeys(keys), byteStream, new ObjectMetadata());
/*AWS SDK for Java v2 migration: When using InputStream to upload with S3Client, Content-Length should be specified and used with RequestBody.fromInputStream(). Otherwise, the entire stream will be buffered in memory. If content length must be unknown, we recommend using the CRT-based S3 client - https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/crt-based-s3-client.html*/
s3client.putObject(PutObjectRequest.builder().bucket(bucketName).key(resolveKeys(keys)).build(), RequestBody.fromContentProvider(() -> byteStream, "application/octet-stream"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@Override
public void delete(String[] keys) {
s3client.deleteObject(bucketName, resolveKeys(keys));
s3client.deleteObject(DeleteObjectRequest.builder().bucket(bucketName).key(resolveKeys(keys))
.build());
}

@Override
public Stream<String> list(String[] keys) {
final String fullKey = resolveKeys(keys);
return s3client.listObjects(bucketName, fullKey)
.getObjectSummaries()
ListObjectsRequest req = ListObjectsRequest.builder()
.bucket(bucketName).prefix(fullKey)
.build();
ListObjectsResponse res = s3client.listObjects(req);
return res.contents()
.stream()
.map(p -> p.getKey().substring(fullKey.length() + 1));
.map(p -> p.key().substring(fullKey.length() + 1));
}

@Nonnull
Expand Down
13 changes: 7 additions & 6 deletions src/test/java/dev/zarr/zarrjava/ZarrTest.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package dev.zarr.zarrjava;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.AnonymousAWSCredentials;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.luben.zstd.Zstd;
Expand All @@ -20,6 +17,10 @@
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;

import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import ucar.ma2.MAMath;

import java.io.*;
Expand Down Expand Up @@ -280,9 +281,9 @@ public void testFileSystemStores() throws IOException, ZarrException {

@Test
public void testS3Store() throws IOException, ZarrException {
S3Store s3Store = new S3Store(AmazonS3ClientBuilder.standard()
.withRegion("eu-west-1")
.withCredentials(new AWSStaticCredentialsProvider(new AnonymousAWSCredentials()))
S3Store s3Store = new S3Store(S3Client.builder()
.region(Region.of("eu-west-1"))
.credentialsProvider(AnonymousCredentialsProvider.create())
.build(), "static.webknossos.org", "data");
System.out.println(Array.open(s3Store.resolve("zarr_v3", "l4_sample", "color", "1")));
}
Expand Down
Loading