aws-sdk-s3 1.176.1 → 1.208.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +202 -0
- data/VERSION +1 -1
- data/lib/aws-sdk-s3/bucket.rb +86 -35
- data/lib/aws-sdk-s3/bucket_acl.rb +7 -6
- data/lib/aws-sdk-s3/bucket_cors.rb +6 -5
- data/lib/aws-sdk-s3/bucket_lifecycle.rb +2 -2
- data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +3 -3
- data/lib/aws-sdk-s3/bucket_logging.rb +2 -2
- data/lib/aws-sdk-s3/bucket_policy.rb +6 -5
- data/lib/aws-sdk-s3/bucket_request_payment.rb +3 -3
- data/lib/aws-sdk-s3/bucket_tagging.rb +3 -3
- data/lib/aws-sdk-s3/bucket_versioning.rb +42 -9
- data/lib/aws-sdk-s3/bucket_website.rb +3 -3
- data/lib/aws-sdk-s3/client.rb +3038 -1226
- data/lib/aws-sdk-s3/client_api.rb +492 -164
- data/lib/aws-sdk-s3/customizations/object.rb +76 -86
- data/lib/aws-sdk-s3/customizations.rb +4 -1
- data/lib/aws-sdk-s3/default_executor.rb +103 -0
- data/lib/aws-sdk-s3/encryption/client.rb +2 -2
- data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +2 -0
- data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +2 -0
- data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +2 -0
- data/lib/aws-sdk-s3/encryptionV2/client.rb +98 -23
- data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +7 -162
- data/lib/aws-sdk-s3/encryptionV2/decryption.rb +205 -0
- data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +17 -0
- data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +2 -0
- data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +2 -0
- data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +8 -0
- data/lib/aws-sdk-s3/encryptionV2/utils.rb +5 -0
- data/lib/aws-sdk-s3/encryptionV3/client.rb +885 -0
- data/lib/aws-sdk-s3/encryptionV3/decrypt_handler.rb +98 -0
- data/lib/aws-sdk-s3/encryptionV3/decryption.rb +244 -0
- data/lib/aws-sdk-s3/encryptionV3/default_cipher_provider.rb +159 -0
- data/lib/aws-sdk-s3/encryptionV3/default_key_provider.rb +35 -0
- data/lib/aws-sdk-s3/encryptionV3/encrypt_handler.rb +98 -0
- data/lib/aws-sdk-s3/encryptionV3/errors.rb +47 -0
- data/lib/aws-sdk-s3/encryptionV3/io_auth_decrypter.rb +60 -0
- data/lib/aws-sdk-s3/encryptionV3/io_decrypter.rb +35 -0
- data/lib/aws-sdk-s3/encryptionV3/io_encrypter.rb +84 -0
- data/lib/aws-sdk-s3/encryptionV3/key_provider.rb +28 -0
- data/lib/aws-sdk-s3/encryptionV3/kms_cipher_provider.rb +159 -0
- data/lib/aws-sdk-s3/encryptionV3/materials.rb +58 -0
- data/lib/aws-sdk-s3/encryptionV3/utils.rb +321 -0
- data/lib/aws-sdk-s3/encryption_v2.rb +1 -0
- data/lib/aws-sdk-s3/encryption_v3.rb +24 -0
- data/lib/aws-sdk-s3/endpoint_parameters.rb +17 -17
- data/lib/aws-sdk-s3/endpoint_provider.rb +562 -304
- data/lib/aws-sdk-s3/endpoints.rb +110 -0
- data/lib/aws-sdk-s3/errors.rb +11 -0
- data/lib/aws-sdk-s3/file_downloader.rb +189 -143
- data/lib/aws-sdk-s3/file_uploader.rb +9 -13
- data/lib/aws-sdk-s3/legacy_signer.rb +2 -1
- data/lib/aws-sdk-s3/multipart_download_error.rb +8 -0
- data/lib/aws-sdk-s3/multipart_file_uploader.rb +105 -102
- data/lib/aws-sdk-s3/multipart_stream_uploader.rb +96 -107
- data/lib/aws-sdk-s3/multipart_upload.rb +50 -6
- data/lib/aws-sdk-s3/multipart_upload_error.rb +3 -4
- data/lib/aws-sdk-s3/multipart_upload_part.rb +50 -34
- data/lib/aws-sdk-s3/object.rb +264 -137
- data/lib/aws-sdk-s3/object_acl.rb +12 -6
- data/lib/aws-sdk-s3/object_multipart_copier.rb +2 -1
- data/lib/aws-sdk-s3/object_summary.rb +179 -103
- data/lib/aws-sdk-s3/object_version.rb +25 -23
- data/lib/aws-sdk-s3/plugins/checksum_algorithm.rb +31 -0
- data/lib/aws-sdk-s3/plugins/endpoints.rb +1 -1
- data/lib/aws-sdk-s3/plugins/express_session_auth.rb +11 -20
- data/lib/aws-sdk-s3/plugins/md5s.rb +10 -71
- data/lib/aws-sdk-s3/plugins/streaming_retry.rb +5 -7
- data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +2 -1
- data/lib/aws-sdk-s3/presigner.rb +4 -5
- data/lib/aws-sdk-s3/resource.rb +7 -1
- data/lib/aws-sdk-s3/transfer_manager.rb +303 -0
- data/lib/aws-sdk-s3/types.rb +2907 -1059
- data/lib/aws-sdk-s3.rb +1 -1
- data/sig/bucket.rbs +16 -6
- data/sig/bucket_acl.rbs +1 -1
- data/sig/bucket_cors.rbs +1 -1
- data/sig/bucket_lifecycle.rbs +1 -1
- data/sig/bucket_lifecycle_configuration.rbs +1 -1
- data/sig/bucket_logging.rbs +1 -1
- data/sig/bucket_policy.rbs +1 -1
- data/sig/bucket_request_payment.rbs +1 -1
- data/sig/bucket_tagging.rbs +1 -1
- data/sig/bucket_versioning.rbs +3 -3
- data/sig/bucket_website.rbs +1 -1
- data/sig/client.rbs +226 -64
- data/sig/errors.rbs +2 -0
- data/sig/multipart_upload.rbs +9 -2
- data/sig/multipart_upload_part.rbs +5 -1
- data/sig/object.rbs +31 -15
- data/sig/object_acl.rbs +1 -1
- data/sig/object_summary.rbs +22 -15
- data/sig/object_version.rbs +5 -2
- data/sig/resource.rbs +11 -2
- data/sig/types.rbs +281 -64
- metadata +26 -10
- data/lib/aws-sdk-s3/plugins/skip_whole_multipart_get_checksums.rb +0 -31
data/lib/aws-sdk-s3/endpoints.rb
CHANGED
|
@@ -63,6 +63,18 @@ module Aws::S3
|
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
class CreateBucketMetadataConfiguration
|
|
67
|
+
def self.build(context)
|
|
68
|
+
Aws::S3::EndpointParameters.create(
|
|
69
|
+
context.config,
|
|
70
|
+
bucket: context.params[:bucket],
|
|
71
|
+
use_dual_stack: context[:use_dualstack_endpoint],
|
|
72
|
+
accelerate: context[:use_accelerate_endpoint],
|
|
73
|
+
use_s3_express_control_endpoint: true,
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
66
78
|
class CreateBucketMetadataTableConfiguration
|
|
67
79
|
def self.build(context)
|
|
68
80
|
Aws::S3::EndpointParameters.create(
|
|
@@ -183,6 +195,18 @@ module Aws::S3
|
|
|
183
195
|
end
|
|
184
196
|
end
|
|
185
197
|
|
|
198
|
+
class DeleteBucketMetadataConfiguration
|
|
199
|
+
def self.build(context)
|
|
200
|
+
Aws::S3::EndpointParameters.create(
|
|
201
|
+
context.config,
|
|
202
|
+
bucket: context.params[:bucket],
|
|
203
|
+
use_dual_stack: context[:use_dualstack_endpoint],
|
|
204
|
+
accelerate: context[:use_accelerate_endpoint],
|
|
205
|
+
use_s3_express_control_endpoint: true,
|
|
206
|
+
)
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
186
210
|
class DeleteBucketMetadataTableConfiguration
|
|
187
211
|
def self.build(context)
|
|
188
212
|
Aws::S3::EndpointParameters.create(
|
|
@@ -313,6 +337,17 @@ module Aws::S3
|
|
|
313
337
|
end
|
|
314
338
|
end
|
|
315
339
|
|
|
340
|
+
class GetBucketAbac
|
|
341
|
+
def self.build(context)
|
|
342
|
+
Aws::S3::EndpointParameters.create(
|
|
343
|
+
context.config,
|
|
344
|
+
bucket: context.params[:bucket],
|
|
345
|
+
use_dual_stack: context[:use_dualstack_endpoint],
|
|
346
|
+
accelerate: context[:use_accelerate_endpoint],
|
|
347
|
+
)
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
316
351
|
class GetBucketAccelerateConfiguration
|
|
317
352
|
def self.build(context)
|
|
318
353
|
Aws::S3::EndpointParameters.create(
|
|
@@ -445,6 +480,18 @@ module Aws::S3
|
|
|
445
480
|
end
|
|
446
481
|
end
|
|
447
482
|
|
|
483
|
+
class GetBucketMetadataConfiguration
|
|
484
|
+
def self.build(context)
|
|
485
|
+
Aws::S3::EndpointParameters.create(
|
|
486
|
+
context.config,
|
|
487
|
+
bucket: context.params[:bucket],
|
|
488
|
+
use_dual_stack: context[:use_dualstack_endpoint],
|
|
489
|
+
accelerate: context[:use_accelerate_endpoint],
|
|
490
|
+
use_s3_express_control_endpoint: true,
|
|
491
|
+
)
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
|
|
448
495
|
class GetBucketMetadataTableConfiguration
|
|
449
496
|
def self.build(context)
|
|
450
497
|
Aws::S3::EndpointParameters.create(
|
|
@@ -842,6 +889,17 @@ module Aws::S3
|
|
|
842
889
|
end
|
|
843
890
|
end
|
|
844
891
|
|
|
892
|
+
class PutBucketAbac
|
|
893
|
+
def self.build(context)
|
|
894
|
+
Aws::S3::EndpointParameters.create(
|
|
895
|
+
context.config,
|
|
896
|
+
bucket: context.params[:bucket],
|
|
897
|
+
use_dual_stack: context[:use_dualstack_endpoint],
|
|
898
|
+
accelerate: context[:use_accelerate_endpoint],
|
|
899
|
+
)
|
|
900
|
+
end
|
|
901
|
+
end
|
|
902
|
+
|
|
845
903
|
class PutBucketAccelerateConfiguration
|
|
846
904
|
def self.build(context)
|
|
847
905
|
Aws::S3::EndpointParameters.create(
|
|
@@ -1162,6 +1220,18 @@ module Aws::S3
|
|
|
1162
1220
|
end
|
|
1163
1221
|
end
|
|
1164
1222
|
|
|
1223
|
+
class RenameObject
|
|
1224
|
+
def self.build(context)
|
|
1225
|
+
Aws::S3::EndpointParameters.create(
|
|
1226
|
+
context.config,
|
|
1227
|
+
bucket: context.params[:bucket],
|
|
1228
|
+
use_dual_stack: context[:use_dualstack_endpoint],
|
|
1229
|
+
accelerate: context[:use_accelerate_endpoint],
|
|
1230
|
+
key: context.params[:key],
|
|
1231
|
+
)
|
|
1232
|
+
end
|
|
1233
|
+
end
|
|
1234
|
+
|
|
1165
1235
|
class RestoreObject
|
|
1166
1236
|
def self.build(context)
|
|
1167
1237
|
Aws::S3::EndpointParameters.create(
|
|
@@ -1184,6 +1254,30 @@ module Aws::S3
|
|
|
1184
1254
|
end
|
|
1185
1255
|
end
|
|
1186
1256
|
|
|
1257
|
+
class UpdateBucketMetadataInventoryTableConfiguration
|
|
1258
|
+
def self.build(context)
|
|
1259
|
+
Aws::S3::EndpointParameters.create(
|
|
1260
|
+
context.config,
|
|
1261
|
+
bucket: context.params[:bucket],
|
|
1262
|
+
use_dual_stack: context[:use_dualstack_endpoint],
|
|
1263
|
+
accelerate: context[:use_accelerate_endpoint],
|
|
1264
|
+
use_s3_express_control_endpoint: true,
|
|
1265
|
+
)
|
|
1266
|
+
end
|
|
1267
|
+
end
|
|
1268
|
+
|
|
1269
|
+
class UpdateBucketMetadataJournalTableConfiguration
|
|
1270
|
+
def self.build(context)
|
|
1271
|
+
Aws::S3::EndpointParameters.create(
|
|
1272
|
+
context.config,
|
|
1273
|
+
bucket: context.params[:bucket],
|
|
1274
|
+
use_dual_stack: context[:use_dualstack_endpoint],
|
|
1275
|
+
accelerate: context[:use_accelerate_endpoint],
|
|
1276
|
+
use_s3_express_control_endpoint: true,
|
|
1277
|
+
)
|
|
1278
|
+
end
|
|
1279
|
+
end
|
|
1280
|
+
|
|
1187
1281
|
class UploadPart
|
|
1188
1282
|
def self.build(context)
|
|
1189
1283
|
Aws::S3::EndpointParameters.create(
|
|
@@ -1230,6 +1324,8 @@ module Aws::S3
|
|
|
1230
1324
|
CopyObject.build(context)
|
|
1231
1325
|
when :create_bucket
|
|
1232
1326
|
CreateBucket.build(context)
|
|
1327
|
+
when :create_bucket_metadata_configuration
|
|
1328
|
+
CreateBucketMetadataConfiguration.build(context)
|
|
1233
1329
|
when :create_bucket_metadata_table_configuration
|
|
1234
1330
|
CreateBucketMetadataTableConfiguration.build(context)
|
|
1235
1331
|
when :create_multipart_upload
|
|
@@ -1250,6 +1346,8 @@ module Aws::S3
|
|
|
1250
1346
|
DeleteBucketInventoryConfiguration.build(context)
|
|
1251
1347
|
when :delete_bucket_lifecycle
|
|
1252
1348
|
DeleteBucketLifecycle.build(context)
|
|
1349
|
+
when :delete_bucket_metadata_configuration
|
|
1350
|
+
DeleteBucketMetadataConfiguration.build(context)
|
|
1253
1351
|
when :delete_bucket_metadata_table_configuration
|
|
1254
1352
|
DeleteBucketMetadataTableConfiguration.build(context)
|
|
1255
1353
|
when :delete_bucket_metrics_configuration
|
|
@@ -1272,6 +1370,8 @@ module Aws::S3
|
|
|
1272
1370
|
DeleteObjects.build(context)
|
|
1273
1371
|
when :delete_public_access_block
|
|
1274
1372
|
DeletePublicAccessBlock.build(context)
|
|
1373
|
+
when :get_bucket_abac
|
|
1374
|
+
GetBucketAbac.build(context)
|
|
1275
1375
|
when :get_bucket_accelerate_configuration
|
|
1276
1376
|
GetBucketAccelerateConfiguration.build(context)
|
|
1277
1377
|
when :get_bucket_acl
|
|
@@ -1294,6 +1394,8 @@ module Aws::S3
|
|
|
1294
1394
|
GetBucketLocation.build(context)
|
|
1295
1395
|
when :get_bucket_logging
|
|
1296
1396
|
GetBucketLogging.build(context)
|
|
1397
|
+
when :get_bucket_metadata_configuration
|
|
1398
|
+
GetBucketMetadataConfiguration.build(context)
|
|
1297
1399
|
when :get_bucket_metadata_table_configuration
|
|
1298
1400
|
GetBucketMetadataTableConfiguration.build(context)
|
|
1299
1401
|
when :get_bucket_metrics_configuration
|
|
@@ -1362,6 +1464,8 @@ module Aws::S3
|
|
|
1362
1464
|
ListObjectsV2.build(context)
|
|
1363
1465
|
when :list_parts
|
|
1364
1466
|
ListParts.build(context)
|
|
1467
|
+
when :put_bucket_abac
|
|
1468
|
+
PutBucketAbac.build(context)
|
|
1365
1469
|
when :put_bucket_accelerate_configuration
|
|
1366
1470
|
PutBucketAccelerateConfiguration.build(context)
|
|
1367
1471
|
when :put_bucket_acl
|
|
@@ -1416,10 +1520,16 @@ module Aws::S3
|
|
|
1416
1520
|
PutObjectTagging.build(context)
|
|
1417
1521
|
when :put_public_access_block
|
|
1418
1522
|
PutPublicAccessBlock.build(context)
|
|
1523
|
+
when :rename_object
|
|
1524
|
+
RenameObject.build(context)
|
|
1419
1525
|
when :restore_object
|
|
1420
1526
|
RestoreObject.build(context)
|
|
1421
1527
|
when :select_object_content
|
|
1422
1528
|
SelectObjectContent.build(context)
|
|
1529
|
+
when :update_bucket_metadata_inventory_table_configuration
|
|
1530
|
+
UpdateBucketMetadataInventoryTableConfiguration.build(context)
|
|
1531
|
+
when :update_bucket_metadata_journal_table_configuration
|
|
1532
|
+
UpdateBucketMetadataJournalTableConfiguration.build(context)
|
|
1423
1533
|
when :upload_part
|
|
1424
1534
|
UploadPart.build(context)
|
|
1425
1535
|
when :upload_part_copy
|
data/lib/aws-sdk-s3/errors.rb
CHANGED
|
@@ -30,6 +30,7 @@ module Aws::S3
|
|
|
30
30
|
# * {BucketAlreadyExists}
|
|
31
31
|
# * {BucketAlreadyOwnedByYou}
|
|
32
32
|
# * {EncryptionTypeMismatch}
|
|
33
|
+
# * {IdempotencyParameterMismatch}
|
|
33
34
|
# * {InvalidObjectState}
|
|
34
35
|
# * {InvalidRequest}
|
|
35
36
|
# * {InvalidWriteOffset}
|
|
@@ -76,6 +77,16 @@ module Aws::S3
|
|
|
76
77
|
end
|
|
77
78
|
end
|
|
78
79
|
|
|
80
|
+
class IdempotencyParameterMismatch < ServiceError
|
|
81
|
+
|
|
82
|
+
# @param [Seahorse::Client::RequestContext] context
|
|
83
|
+
# @param [String] message
|
|
84
|
+
# @param [Aws::S3::Types::IdempotencyParameterMismatch] data
|
|
85
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
|
86
|
+
super(context, message, data)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
79
90
|
class InvalidObjectState < ServiceError
|
|
80
91
|
|
|
81
92
|
# @param [Seahorse::Client::RequestContext] context
|
|
@@ -1,226 +1,270 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'pathname'
|
|
4
|
-
require '
|
|
4
|
+
require 'securerandom'
|
|
5
5
|
require 'set'
|
|
6
|
-
require 'tmpdir'
|
|
7
6
|
|
|
8
7
|
module Aws
|
|
9
8
|
module S3
|
|
10
9
|
# @api private
|
|
11
10
|
class FileDownloader
|
|
12
|
-
|
|
13
11
|
MIN_CHUNK_SIZE = 5 * 1024 * 1024
|
|
14
12
|
MAX_PARTS = 10_000
|
|
15
|
-
|
|
13
|
+
HEAD_OPTIONS = Set.new(Client.api.operation(:head_object).input.shape.member_names)
|
|
14
|
+
GET_OPTIONS = Set.new(Client.api.operation(:get_object).input.shape.member_names)
|
|
16
15
|
|
|
17
16
|
def initialize(options = {})
|
|
18
17
|
@client = options[:client] || Client.new
|
|
18
|
+
@executor = options[:executor]
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
# @return [Client]
|
|
22
22
|
attr_reader :client
|
|
23
23
|
|
|
24
24
|
def download(destination, options = {})
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
@chunk_size = options[:chunk_size]
|
|
29
|
-
@params = {
|
|
30
|
-
bucket: options[:bucket],
|
|
31
|
-
key: options[:key],
|
|
32
|
-
}
|
|
33
|
-
@params[:version_id] = options[:version_id] if options[:version_id]
|
|
25
|
+
validate_destination!(destination)
|
|
26
|
+
opts = build_download_opts(destination, options)
|
|
27
|
+
validate_opts!(opts)
|
|
34
28
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
Aws::Plugins::UserAgent.metric('S3_TRANSFER') do
|
|
30
|
+
case opts[:mode]
|
|
31
|
+
when 'auto' then multipart_download(opts)
|
|
32
|
+
when 'single_request' then single_request(opts)
|
|
33
|
+
when 'get_range' then range_request(opts)
|
|
34
|
+
end
|
|
40
35
|
end
|
|
41
|
-
|
|
36
|
+
File.rename(opts[:temp_path], destination) if opts[:temp_path]
|
|
37
|
+
ensure
|
|
38
|
+
cleanup_temp_file(opts)
|
|
39
|
+
end
|
|
42
40
|
|
|
43
|
-
|
|
41
|
+
private
|
|
44
42
|
|
|
45
|
-
|
|
43
|
+
def build_download_opts(destination, opts)
|
|
44
|
+
{
|
|
45
|
+
destination: destination,
|
|
46
|
+
mode: opts.delete(:mode) || 'auto',
|
|
47
|
+
chunk_size: opts.delete(:chunk_size),
|
|
48
|
+
on_checksum_validated: opts.delete(:on_checksum_validated),
|
|
49
|
+
progress_callback: opts.delete(:progress_callback),
|
|
50
|
+
params: opts,
|
|
51
|
+
temp_path: nil
|
|
52
|
+
}
|
|
53
|
+
end
|
|
46
54
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
def cleanup_temp_file(opts)
|
|
56
|
+
return unless opts
|
|
57
|
+
|
|
58
|
+
temp_file = opts[:temp_path]
|
|
59
|
+
File.delete(temp_file) if temp_file && File.exist?(temp_file)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def download_with_executor(part_list, total_size, opts)
|
|
63
|
+
download_attempts = 0
|
|
64
|
+
completion_queue = Queue.new
|
|
65
|
+
abort_download = false
|
|
66
|
+
error = nil
|
|
67
|
+
progress = MultipartProgress.new(part_list, total_size, opts[:progress_callback])
|
|
68
|
+
|
|
69
|
+
while (part = part_list.shift)
|
|
70
|
+
break if abort_download
|
|
71
|
+
|
|
72
|
+
download_attempts += 1
|
|
73
|
+
@executor.post(part) do |p|
|
|
74
|
+
update_progress(progress, p)
|
|
75
|
+
resp = @client.get_object(p.params)
|
|
76
|
+
range = extract_range(resp.content_range)
|
|
77
|
+
validate_range(range, p.params[:range]) if p.params[:range]
|
|
78
|
+
write(resp.body, range, opts)
|
|
79
|
+
|
|
80
|
+
execute_checksum_callback(resp, opts)
|
|
81
|
+
rescue StandardError => e
|
|
82
|
+
abort_download = true
|
|
83
|
+
error = e
|
|
84
|
+
ensure
|
|
85
|
+
completion_queue << :done
|
|
63
86
|
end
|
|
64
87
|
end
|
|
88
|
+
|
|
89
|
+
download_attempts.times { completion_queue.pop }
|
|
90
|
+
raise error unless error.nil?
|
|
65
91
|
end
|
|
66
92
|
|
|
67
|
-
|
|
93
|
+
def handle_checksum_mode_option(option_key, opts)
|
|
94
|
+
return false unless option_key == :checksum_mode && opts[:checksum_mode] == 'DISABLED'
|
|
95
|
+
|
|
96
|
+
msg = ':checksum_mode option is deprecated. Checksums will be validated by default. ' \
|
|
97
|
+
'To disable checksum validation, set :response_checksum_validation to "when_required" on your S3 client.'
|
|
98
|
+
warn(msg)
|
|
99
|
+
true
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def get_opts(opts)
|
|
103
|
+
GET_OPTIONS.each_with_object({}) do |k, h|
|
|
104
|
+
next if k == :checksum_mode
|
|
68
105
|
|
|
69
|
-
|
|
70
|
-
if @on_checksum_validated && @params[:checksum_mode] != 'ENABLED'
|
|
71
|
-
raise ArgumentError, "You must set checksum_mode: 'ENABLED' " +
|
|
72
|
-
"when providing a on_checksum_validated callback"
|
|
106
|
+
h[k] = opts[k] if opts.key?(k)
|
|
73
107
|
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def head_opts(opts)
|
|
111
|
+
HEAD_OPTIONS.each_with_object({}) do |k, h|
|
|
112
|
+
next if handle_checksum_mode_option(k, opts)
|
|
113
|
+
|
|
114
|
+
h[k] = opts[k] if opts.key?(k)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def compute_chunk(chunk_size, file_size)
|
|
119
|
+
raise ArgumentError, ":chunk_size shouldn't exceed total file size." if chunk_size && chunk_size > file_size
|
|
120
|
+
|
|
121
|
+
chunk_size || [(file_size.to_f / MAX_PARTS).ceil, MIN_CHUNK_SIZE].max.to_i
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def compute_mode(file_size, total_parts, etag, opts)
|
|
125
|
+
chunk_size = compute_chunk(opts[:chunk_size], file_size)
|
|
126
|
+
part_size = (file_size.to_f / total_parts).ceil
|
|
74
127
|
|
|
75
|
-
|
|
76
|
-
|
|
128
|
+
resolve_temp_path(opts)
|
|
129
|
+
if chunk_size < part_size
|
|
130
|
+
multithreaded_get_by_ranges(file_size, etag, opts)
|
|
131
|
+
else
|
|
132
|
+
multithreaded_get_by_parts(total_parts, file_size, etag, opts)
|
|
77
133
|
end
|
|
78
134
|
end
|
|
79
135
|
|
|
80
|
-
def
|
|
81
|
-
|
|
136
|
+
def extract_range(value)
|
|
137
|
+
value.match(%r{bytes (?<range>\d+-\d+)/\d+})[:range]
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def multipart_download(opts)
|
|
141
|
+
resp = @client.head_object(head_opts(opts[:params].merge(part_number: 1)))
|
|
82
142
|
count = resp.parts_count
|
|
143
|
+
|
|
83
144
|
if count.nil? || count <= 1
|
|
84
145
|
if resp.content_length <= MIN_CHUNK_SIZE
|
|
85
|
-
single_request
|
|
146
|
+
single_request(opts)
|
|
86
147
|
else
|
|
87
|
-
|
|
148
|
+
resolve_temp_path(opts)
|
|
149
|
+
multithreaded_get_by_ranges(resp.content_length, resp.etag, opts)
|
|
88
150
|
end
|
|
89
151
|
else
|
|
90
|
-
#
|
|
91
|
-
resp = @client.head_object(
|
|
152
|
+
# covers cases when given object is not uploaded via UploadPart API
|
|
153
|
+
resp = @client.head_object(head_opts(opts[:params])) # partNumber is an option
|
|
92
154
|
if resp.content_length <= MIN_CHUNK_SIZE
|
|
93
|
-
single_request
|
|
155
|
+
single_request(opts)
|
|
94
156
|
else
|
|
95
|
-
compute_mode(resp.content_length, count)
|
|
157
|
+
compute_mode(resp.content_length, count, resp.etag, opts)
|
|
96
158
|
end
|
|
97
159
|
end
|
|
98
160
|
end
|
|
99
161
|
|
|
100
|
-
def
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
multithreaded_get_by_ranges(file_size)
|
|
105
|
-
else
|
|
106
|
-
multithreaded_get_by_parts(count, file_size)
|
|
162
|
+
def multithreaded_get_by_parts(total_parts, file_size, etag, opts)
|
|
163
|
+
parts = (1..total_parts).map do |part|
|
|
164
|
+
params = get_opts(opts[:params].merge(part_number: part, if_match: etag))
|
|
165
|
+
Part.new(part_number: part, params: params)
|
|
107
166
|
end
|
|
167
|
+
download_with_executor(PartList.new(parts), file_size, opts)
|
|
108
168
|
end
|
|
109
169
|
|
|
110
|
-
def
|
|
170
|
+
def multithreaded_get_by_ranges(file_size, etag, opts)
|
|
111
171
|
offset = 0
|
|
112
|
-
default_chunk_size = compute_chunk(file_size)
|
|
172
|
+
default_chunk_size = compute_chunk(opts[:chunk_size], file_size)
|
|
113
173
|
chunks = []
|
|
174
|
+
part_number = 1 # parts start at 1
|
|
114
175
|
while offset < file_size
|
|
115
176
|
progress = offset + default_chunk_size
|
|
116
177
|
progress = file_size if progress > file_size
|
|
117
|
-
|
|
178
|
+
params = get_opts(opts[:params].merge(range: "bytes=#{offset}-#{progress - 1}", if_match: etag))
|
|
179
|
+
chunks << Part.new(part_number: part_number, size: (progress - offset), params: params)
|
|
180
|
+
part_number += 1
|
|
118
181
|
offset = progress
|
|
119
182
|
end
|
|
120
|
-
chunks
|
|
183
|
+
download_with_executor(PartList.new(chunks), file_size, opts)
|
|
121
184
|
end
|
|
122
185
|
|
|
123
|
-
def
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
@chunk_size || [
|
|
128
|
-
(file_size.to_f / MAX_PARTS).ceil, MIN_CHUNK_SIZE
|
|
129
|
-
].max.to_i
|
|
130
|
-
end
|
|
186
|
+
def range_request(opts)
|
|
187
|
+
resp = @client.head_object(head_opts(opts[:params]))
|
|
188
|
+
resolve_temp_path(opts)
|
|
189
|
+
multithreaded_get_by_ranges(resp.content_length, resp.etag, opts)
|
|
131
190
|
end
|
|
132
191
|
|
|
133
|
-
def
|
|
134
|
-
|
|
135
|
-
|
|
192
|
+
def resolve_temp_path(opts)
|
|
193
|
+
return if [File, Tempfile].include?(opts[:destination].class)
|
|
194
|
+
|
|
195
|
+
opts[:temp_path] ||= "#{opts[:destination]}.s3tmp.#{SecureRandom.alphanumeric(8)}"
|
|
136
196
|
end
|
|
137
197
|
|
|
138
|
-
def
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
range = "bytes=#{offset}-#{progress - 1}"
|
|
147
|
-
chunks << Part.new(
|
|
148
|
-
part_number: part_number,
|
|
149
|
-
size: (progress-offset),
|
|
150
|
-
params: @params.merge(range: range)
|
|
151
|
-
)
|
|
152
|
-
part_number += 1
|
|
153
|
-
offset = progress
|
|
154
|
-
end
|
|
155
|
-
download_in_threads(PartList.new(chunks), file_size)
|
|
198
|
+
def single_request(opts)
|
|
199
|
+
params = get_opts(opts[:params]).merge(response_target: opts[:destination])
|
|
200
|
+
params[:on_chunk_received] = single_part_progress(opts) if opts[:progress_callback]
|
|
201
|
+
resp = @client.get_object(params)
|
|
202
|
+
return resp unless opts[:on_checksum_validated]
|
|
203
|
+
|
|
204
|
+
opts[:on_checksum_validated].call(resp.checksum_validated, resp) if resp.checksum_validated
|
|
205
|
+
resp
|
|
156
206
|
end
|
|
157
207
|
|
|
158
|
-
def
|
|
159
|
-
|
|
160
|
-
|
|
208
|
+
def single_part_progress(opts)
|
|
209
|
+
proc do |_chunk, bytes_read, total_size|
|
|
210
|
+
opts[:progress_callback].call([bytes_read], [total_size], total_size)
|
|
161
211
|
end
|
|
162
|
-
download_in_threads(PartList.new(parts), total_size)
|
|
163
212
|
end
|
|
164
213
|
|
|
165
|
-
def
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
thread = Thread.new do
|
|
172
|
-
begin
|
|
173
|
-
while part = pending.shift
|
|
174
|
-
if progress
|
|
175
|
-
part.params[:on_chunk_received] =
|
|
176
|
-
proc do |_chunk, bytes, total|
|
|
177
|
-
progress.call(part.part_number, bytes, total)
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
resp = @client.get_object(part.params)
|
|
181
|
-
write(resp)
|
|
182
|
-
if @on_checksum_validated && resp.checksum_validated
|
|
183
|
-
@on_checksum_validated.call(resp.checksum_validated, resp)
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
nil
|
|
187
|
-
rescue => error
|
|
188
|
-
# keep other threads from downloading other parts
|
|
189
|
-
pending.clear!
|
|
190
|
-
raise error
|
|
191
|
-
end
|
|
214
|
+
def update_progress(progress, part)
|
|
215
|
+
return unless progress.progress_callback
|
|
216
|
+
|
|
217
|
+
part.params[:on_chunk_received] =
|
|
218
|
+
proc do |_chunk, bytes, total|
|
|
219
|
+
progress.call(part.part_number, bytes, total)
|
|
192
220
|
end
|
|
193
|
-
threads << thread
|
|
194
|
-
end
|
|
195
|
-
threads.map(&:value).compact
|
|
196
221
|
end
|
|
197
222
|
|
|
198
|
-
def
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
223
|
+
def execute_checksum_callback(resp, opts)
|
|
224
|
+
return unless opts[:on_checksum_validated] && resp.checksum_validated
|
|
225
|
+
|
|
226
|
+
opts[:on_checksum_validated].call(resp.checksum_validated, resp)
|
|
202
227
|
end
|
|
203
228
|
|
|
204
|
-
def
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
resp = @client.get_object(params)
|
|
229
|
+
def validate_destination!(destination)
|
|
230
|
+
valid_types = [String, Pathname, File, Tempfile]
|
|
231
|
+
return if valid_types.include?(destination.class)
|
|
208
232
|
|
|
209
|
-
|
|
233
|
+
raise ArgumentError, "Invalid destination, expected #{valid_types.join(', ')} but got: #{destination.class}"
|
|
234
|
+
end
|
|
210
235
|
|
|
211
|
-
|
|
212
|
-
|
|
236
|
+
def validate_opts!(opts)
|
|
237
|
+
if opts[:on_checksum_validated] && !opts[:on_checksum_validated].respond_to?(:call)
|
|
238
|
+
raise ArgumentError, ':on_checksum_validated must be callable'
|
|
213
239
|
end
|
|
214
240
|
|
|
215
|
-
|
|
216
|
-
|
|
241
|
+
valid_modes = %w[auto get_range single_request]
|
|
242
|
+
unless valid_modes.include?(opts[:mode])
|
|
243
|
+
msg = "Invalid mode #{opts[:mode]} provided, :mode should be single_request, get_range or auto"
|
|
244
|
+
raise ArgumentError, msg
|
|
245
|
+
end
|
|
217
246
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
@progress_callback.call([bytes_read], [total_size], total_size)
|
|
247
|
+
if opts[:mode] == 'get_range' && opts[:chunk_size].nil?
|
|
248
|
+
raise ArgumentError, 'In get_range mode, :chunk_size must be provided'
|
|
221
249
|
end
|
|
250
|
+
|
|
251
|
+
if opts[:chunk_size] && opts[:chunk_size] <= 0
|
|
252
|
+
raise ArgumentError, ':chunk_size must be positive'
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def validate_range(actual, expected)
|
|
257
|
+
return if actual == expected.match(/bytes=(?<range>\d+-\d+)/)[:range]
|
|
258
|
+
|
|
259
|
+
raise MultipartDownloadError, "multipart download failed: expected range of #{expected} but got #{actual}"
|
|
222
260
|
end
|
|
223
261
|
|
|
262
|
+
def write(body, range, opts)
|
|
263
|
+
path = opts[:temp_path] || opts[:destination]
|
|
264
|
+
File.write(path, body.read, range.split('-').first.to_i)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# @api private
|
|
224
268
|
class Part < Struct.new(:part_number, :size, :params)
|
|
225
269
|
include Aws::Structure
|
|
226
270
|
end
|
|
@@ -251,7 +295,7 @@ module Aws
|
|
|
251
295
|
end
|
|
252
296
|
|
|
253
297
|
# @api private
|
|
254
|
-
class
|
|
298
|
+
class MultipartProgress
|
|
255
299
|
def initialize(parts, total_size, progress_callback)
|
|
256
300
|
@bytes_received = Array.new(parts.size, 0)
|
|
257
301
|
@part_sizes = parts.map(&:size)
|
|
@@ -259,6 +303,8 @@ module Aws
|
|
|
259
303
|
@progress_callback = progress_callback
|
|
260
304
|
end
|
|
261
305
|
|
|
306
|
+
attr_reader :progress_callback
|
|
307
|
+
|
|
262
308
|
def call(part_number, bytes_received, total)
|
|
263
309
|
# part numbers start at 1
|
|
264
310
|
@bytes_received[part_number - 1] = bytes_received
|