19
19
import io .netty .buffer .CompositeByteBuf ;
20
20
import io .netty .buffer .Unpooled ;
21
21
import io .netty .handler .ssl .OpenSsl ;
22
-
23
22
import java .net .URI ;
24
23
import java .nio .charset .StandardCharsets ;
25
24
import java .time .Duration ;
32
31
import java .util .concurrent .CompletableFuture ;
33
32
import java .util .function .Supplier ;
34
33
import java .util .stream .Collectors ;
35
-
36
34
import org .apache .commons .lang3 .StringUtils ;
37
35
import org .apache .commons .lang3 .tuple .Pair ;
38
36
import software .amazon .awssdk .auth .credentials .AnonymousCredentialsProvider ;
51
49
import software .amazon .awssdk .regions .Region ;
52
50
import software .amazon .awssdk .services .s3 .S3AsyncClient ;
53
51
import software .amazon .awssdk .services .s3 .S3AsyncClientBuilder ;
52
+ import software .amazon .awssdk .services .s3 .model .ChecksumAlgorithm ;
53
+ import software .amazon .awssdk .services .s3 .model .ChecksumMode ;
54
54
import software .amazon .awssdk .services .s3 .model .CompleteMultipartUploadRequest ;
55
55
import software .amazon .awssdk .services .s3 .model .CompletedMultipartUpload ;
56
56
import software .amazon .awssdk .services .s3 .model .CompletedPart ;
@@ -83,6 +83,7 @@ public class AwsObjectStorage extends AbstractObjectStorage {
83
83
public static final String AUTH_TYPE_KEY = "authType" ;
84
84
public static final String STATIC_AUTH_TYPE = "static" ;
85
85
public static final String INSTANCE_AUTH_TYPE = "instance" ;
86
+ public static final String CHECKSUM_ALGORITHM_KEY = "checksumAlgorithm" ;
86
87
87
88
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html
88
89
// The maximum number of keys that can be deleted in a single request is 1000.
@@ -93,6 +94,8 @@ public class AwsObjectStorage extends AbstractObjectStorage {
93
94
private final S3AsyncClient readS3Client ;
94
95
private final S3AsyncClient writeS3Client ;
95
96
97
+ private final ChecksumAlgorithm checksumAlgorithm ;
98
+
96
99
private volatile static InstanceProfileCredentialsProvider instanceProfileCredentialsProvider ;
97
100
98
101
public AwsObjectStorage (BucketURI bucketURI , Map <String , String > tagging ,
@@ -102,6 +105,13 @@ public AwsObjectStorage(BucketURI bucketURI, Map<String, String> tagging,
102
105
this .bucket = bucketURI .bucket ();
103
106
this .tagging = tagging (tagging );
104
107
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
+
105
115
Supplier <S3AsyncClient > clientSupplier = () -> newS3Client (bucketURI .endpoint (), bucketURI .region (), bucketURI .extensionBool (PATH_STYLE_KEY , false ), credentialsProviders , getMaxObjectStorageConcurrency ());
106
116
this .writeS3Client = clientSupplier .get ();
107
117
this .readS3Client = readWriteIsolate ? clientSupplier .get () : writeS3Client ;
@@ -114,6 +124,7 @@ public AwsObjectStorage(S3AsyncClient s3Client, String bucket) {
114
124
this .writeS3Client = s3Client ;
115
125
this .readS3Client = s3Client ;
116
126
this .tagging = null ;
127
+ this .checksumAlgorithm = ChecksumAlgorithm .UNKNOWN_TO_SDK_VERSION ;
117
128
}
118
129
119
130
public static Builder builder () {
@@ -144,9 +155,14 @@ static void checkDeleteObjectsResponse(DeleteObjectsResponse response) throws Ex
144
155
145
156
@ Override
146
157
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
+
148
164
CompletableFuture <ByteBuf > cf = new CompletableFuture <>();
149
- readS3Client .getObject (request , AsyncResponseTransformer .toPublisher ())
165
+ readS3Client .getObject (builder . build () , AsyncResponseTransformer .toPublisher ())
150
166
.thenAccept (responsePublisher -> {
151
167
CompositeByteBuf buf = ByteBufAlloc .compositeByteBuffer ();
152
168
responsePublisher .subscribe (bytes -> {
@@ -174,6 +190,11 @@ CompletableFuture<Void> doWrite(WriteOptions options, String path, ByteBuf data)
174
190
if (null != tagging ) {
175
191
builder .tagging (tagging );
176
192
}
193
+
194
+ if (checksumAlgorithm != ChecksumAlgorithm .UNKNOWN_TO_SDK_VERSION ) {
195
+ builder .checksumAlgorithm (checksumAlgorithm );
196
+ }
197
+
177
198
PutObjectRequest request = builder .build ();
178
199
AsyncRequestBody body = AsyncRequestBody .fromByteBuffersUnsafe (data .nioBuffers ());
179
200
return writeS3Client .putObject (request , body ).thenApply (rst -> null );
@@ -185,6 +206,11 @@ CompletableFuture<String> doCreateMultipartUpload(WriteOptions options, String p
185
206
if (null != tagging ) {
186
207
builder .tagging (tagging );
187
208
}
209
+
210
+ if (checksumAlgorithm != ChecksumAlgorithm .UNKNOWN_TO_SDK_VERSION ) {
211
+ builder .checksumAlgorithm (checksumAlgorithm );
212
+ }
213
+
188
214
CreateMultipartUploadRequest request = builder .build ();
189
215
return writeS3Client .createMultipartUpload (request ).thenApply (CreateMultipartUploadResponse ::uploadId );
190
216
}
@@ -193,10 +219,37 @@ CompletableFuture<String> doCreateMultipartUpload(WriteOptions options, String p
193
219
CompletableFuture <ObjectStorageCompletedPart > doUploadPart (WriteOptions options , String path , String uploadId ,
194
220
int partNumber , ByteBuf part ) {
195
221
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
+ });
200
253
}
201
254
202
255
@ Override
@@ -210,14 +263,14 @@ CompletableFuture<ObjectStorageCompletedPart> doUploadPartCopy(WriteOptions opti
210
263
.apiCallTimeout (Duration .ofMillis (options .apiCallAttemptTimeout ())).build ()
211
264
)
212
265
.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 () ));
214
267
}
215
268
216
269
@ Override
217
270
public CompletableFuture <Void > doCompleteMultipartUpload (WriteOptions options , String path , String uploadId ,
218
271
List <ObjectStorageCompletedPart > parts ) {
219
272
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 ())
221
274
.collect (Collectors .toList ());
222
275
CompletedMultipartUpload multipartUpload = CompletedMultipartUpload .builder ().parts (completedParts ).build ();
223
276
CompleteMultipartUploadRequest request = CompleteMultipartUploadRequest .builder ().bucket (bucket ).key (path ).uploadId (uploadId ).multipartUpload (multipartUpload ).build ();
0 commit comments