1919import io .netty .buffer .CompositeByteBuf ;
2020import io .netty .buffer .Unpooled ;
2121import io .netty .handler .ssl .OpenSsl ;
22-
2322import java .net .URI ;
2423import java .nio .charset .StandardCharsets ;
2524import java .time .Duration ;
3231import java .util .concurrent .CompletableFuture ;
3332import java .util .function .Supplier ;
3433import java .util .stream .Collectors ;
35-
3634import org .apache .commons .lang3 .StringUtils ;
3735import org .apache .commons .lang3 .tuple .Pair ;
3836import software .amazon .awssdk .auth .credentials .AnonymousCredentialsProvider ;
5149import software .amazon .awssdk .regions .Region ;
5250import software .amazon .awssdk .services .s3 .S3AsyncClient ;
5351import software .amazon .awssdk .services .s3 .S3AsyncClientBuilder ;
52+ import software .amazon .awssdk .services .s3 .model .ChecksumAlgorithm ;
53+ import software .amazon .awssdk .services .s3 .model .ChecksumMode ;
5454import software .amazon .awssdk .services .s3 .model .CompleteMultipartUploadRequest ;
5555import software .amazon .awssdk .services .s3 .model .CompletedMultipartUpload ;
5656import software .amazon .awssdk .services .s3 .model .CompletedPart ;
@@ -83,6 +83,7 @@ public class AwsObjectStorage extends AbstractObjectStorage {
8383 public static final String AUTH_TYPE_KEY = "authType" ;
8484 public static final String STATIC_AUTH_TYPE = "static" ;
8585 public static final String INSTANCE_AUTH_TYPE = "instance" ;
86+ public static final String CHECKSUM_ALGORITHM_KEY = "checksumAlgorithm" ;
8687
8788 // https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html
8889 // The maximum number of keys that can be deleted in a single request is 1000.
@@ -93,6 +94,8 @@ public class AwsObjectStorage extends AbstractObjectStorage {
9394 private final S3AsyncClient readS3Client ;
9495 private final S3AsyncClient writeS3Client ;
9596
97+ private final ChecksumAlgorithm checksumAlgorithm ;
98+
9699 private volatile static InstanceProfileCredentialsProvider instanceProfileCredentialsProvider ;
97100
98101 public AwsObjectStorage (BucketURI bucketURI , Map <String , String > tagging ,
@@ -102,6 +105,13 @@ public AwsObjectStorage(BucketURI bucketURI, Map<String, String> tagging,
102105 this .bucket = bucketURI .bucket ();
103106 this .tagging = tagging (tagging );
104107 List <AwsCredentialsProvider > credentialsProviders = credentialsProviders ();
108+
109+ ChecksumAlgorithm checksumAlgorithm = ChecksumAlgorithm .fromValue (bucketURI .extensionString (CHECKSUM_ALGORITHM_KEY ));
110+ if (checksumAlgorithm == null ) {
111+ checksumAlgorithm = ChecksumAlgorithm .UNKNOWN_TO_SDK_VERSION ;
112+ }
113+ this .checksumAlgorithm = checksumAlgorithm ;
114+
105115 Supplier <S3AsyncClient > clientSupplier = () -> newS3Client (bucketURI .endpoint (), bucketURI .region (), bucketURI .extensionBool (PATH_STYLE_KEY , false ), credentialsProviders , getMaxObjectStorageConcurrency ());
106116 this .writeS3Client = clientSupplier .get ();
107117 this .readS3Client = readWriteIsolate ? clientSupplier .get () : writeS3Client ;
@@ -114,6 +124,7 @@ public AwsObjectStorage(S3AsyncClient s3Client, String bucket) {
114124 this .writeS3Client = s3Client ;
115125 this .readS3Client = s3Client ;
116126 this .tagging = null ;
127+ this .checksumAlgorithm = ChecksumAlgorithm .UNKNOWN_TO_SDK_VERSION ;
117128 }
118129
119130 public static Builder builder () {
@@ -144,9 +155,14 @@ static void checkDeleteObjectsResponse(DeleteObjectsResponse response) throws Ex
144155
145156 @ Override
146157 CompletableFuture <ByteBuf > doRangeRead (ReadOptions options , String path , long start , long end ) {
147- GetObjectRequest request = GetObjectRequest .builder ().bucket (bucket ).key (path ).range (range (start , end )).build ();
158+ GetObjectRequest .Builder builder = GetObjectRequest .builder ().bucket (bucket ).key (path ).range (range (start , end ));
159+
160+ if (checksumAlgorithm != ChecksumAlgorithm .UNKNOWN_TO_SDK_VERSION ) {
161+ builder .checksumMode (ChecksumMode .ENABLED );
162+ }
163+
148164 CompletableFuture <ByteBuf > cf = new CompletableFuture <>();
149- readS3Client .getObject (request , AsyncResponseTransformer .toPublisher ())
165+ readS3Client .getObject (builder . build () , AsyncResponseTransformer .toPublisher ())
150166 .thenAccept (responsePublisher -> {
151167 CompositeByteBuf buf = ByteBufAlloc .compositeByteBuffer ();
152168 responsePublisher .subscribe (bytes -> {
@@ -174,6 +190,11 @@ CompletableFuture<Void> doWrite(WriteOptions options, String path, ByteBuf data)
174190 if (null != tagging ) {
175191 builder .tagging (tagging );
176192 }
193+
194+ if (checksumAlgorithm != ChecksumAlgorithm .UNKNOWN_TO_SDK_VERSION ) {
195+ builder .checksumAlgorithm (checksumAlgorithm );
196+ }
197+
177198 PutObjectRequest request = builder .build ();
178199 AsyncRequestBody body = AsyncRequestBody .fromByteBuffersUnsafe (data .nioBuffers ());
179200 return writeS3Client .putObject (request , body ).thenApply (rst -> null );
@@ -185,6 +206,11 @@ CompletableFuture<String> doCreateMultipartUpload(WriteOptions options, String p
185206 if (null != tagging ) {
186207 builder .tagging (tagging );
187208 }
209+
210+ if (checksumAlgorithm != ChecksumAlgorithm .UNKNOWN_TO_SDK_VERSION ) {
211+ builder .checksumAlgorithm (checksumAlgorithm );
212+ }
213+
188214 CreateMultipartUploadRequest request = builder .build ();
189215 return writeS3Client .createMultipartUpload (request ).thenApply (CreateMultipartUploadResponse ::uploadId );
190216 }
@@ -193,10 +219,37 @@ CompletableFuture<String> doCreateMultipartUpload(WriteOptions options, String p
193219 CompletableFuture <ObjectStorageCompletedPart > doUploadPart (WriteOptions options , String path , String uploadId ,
194220 int partNumber , ByteBuf part ) {
195221 AsyncRequestBody body = AsyncRequestBody .fromByteBuffersUnsafe (part .nioBuffers ());
196- UploadPartRequest request = UploadPartRequest .builder ().bucket (bucket ).key (path ).uploadId (uploadId )
197- .partNumber (partNumber ).build ();
198- return writeS3Client .uploadPart (request , body )
199- .thenApply (resp -> new ObjectStorageCompletedPart (partNumber , resp .eTag ()));
222+ UploadPartRequest .Builder builder = UploadPartRequest .builder ()
223+ .bucket (bucket )
224+ .key (path )
225+ .uploadId (uploadId )
226+ .partNumber (partNumber );
227+
228+ if (checksumAlgorithm != ChecksumAlgorithm .UNKNOWN_TO_SDK_VERSION ) {
229+ builder .checksumAlgorithm (checksumAlgorithm );
230+ }
231+
232+ return writeS3Client .uploadPart (builder .build (), body )
233+ .thenApply (resp -> {
234+ String checksum ;
235+ switch (checksumAlgorithm ) {
236+ case CRC32_C :
237+ checksum = resp .checksumCRC32C ();
238+ break ;
239+ case CRC32 :
240+ checksum = resp .checksumCRC32 ();
241+ break ;
242+ case SHA1 :
243+ checksum = resp .checksumSHA1 ();
244+ break ;
245+ case SHA256 :
246+ checksum = resp .checksumSHA256 ();
247+ break ;
248+ default :
249+ checksum = null ;
250+ }
251+ return new ObjectStorageCompletedPart (partNumber , resp .eTag (), checksum );
252+ });
200253 }
201254
202255 @ Override
@@ -210,14 +263,14 @@ CompletableFuture<ObjectStorageCompletedPart> doUploadPartCopy(WriteOptions opti
210263 .apiCallTimeout (Duration .ofMillis (options .apiCallAttemptTimeout ())).build ()
211264 )
212265 .build ();
213- return writeS3Client .uploadPartCopy (request ).thenApply (resp -> new ObjectStorageCompletedPart (partNumber , resp .copyPartResult ().eTag ()));
266+ return writeS3Client .uploadPartCopy (request ).thenApply (resp -> new ObjectStorageCompletedPart (partNumber , resp .copyPartResult ().eTag (), resp . copyPartResult (). checksumCRC32C () ));
214267 }
215268
216269 @ Override
217270 public CompletableFuture <Void > doCompleteMultipartUpload (WriteOptions options , String path , String uploadId ,
218271 List <ObjectStorageCompletedPart > parts ) {
219272 List <CompletedPart > completedParts = parts .stream ()
220- .map (part -> CompletedPart .builder ().partNumber (part .getPartNumber ()).eTag (part .getPartId ()).build ())
273+ .map (part -> CompletedPart .builder ().partNumber (part .getPartNumber ()).eTag (part .getPartId ()).checksumCRC32C ( part . getCheckSum ()). build ())
221274 .collect (Collectors .toList ());
222275 CompletedMultipartUpload multipartUpload = CompletedMultipartUpload .builder ().parts (completedParts ).build ();
223276 CompleteMultipartUploadRequest request = CompleteMultipartUploadRequest .builder ().bucket (bucket ).key (path ).uploadId (uploadId ).multipartUpload (multipartUpload ).build ();
0 commit comments