aws-sdk-s3 1.48.0 → 1.183.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 +5 -5
- data/CHANGELOG.md +1352 -0
- data/LICENSE.txt +202 -0
- data/VERSION +1 -0
- data/lib/aws-sdk-s3/access_grants_credentials.rb +57 -0
- data/lib/aws-sdk-s3/access_grants_credentials_provider.rb +250 -0
- data/lib/aws-sdk-s3/bucket.rb +1005 -106
- data/lib/aws-sdk-s3/bucket_acl.rb +65 -18
- data/lib/aws-sdk-s3/bucket_cors.rb +80 -18
- data/lib/aws-sdk-s3/bucket_lifecycle.rb +71 -20
- data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +126 -21
- data/lib/aws-sdk-s3/bucket_logging.rb +68 -16
- data/lib/aws-sdk-s3/bucket_notification.rb +52 -20
- data/lib/aws-sdk-s3/bucket_policy.rb +108 -17
- data/lib/aws-sdk-s3/bucket_region_cache.rb +11 -5
- data/lib/aws-sdk-s3/bucket_request_payment.rb +60 -15
- data/lib/aws-sdk-s3/bucket_tagging.rb +71 -18
- data/lib/aws-sdk-s3/bucket_versioning.rb +133 -17
- data/lib/aws-sdk-s3/bucket_website.rb +78 -21
- data/lib/aws-sdk-s3/client.rb +14517 -941
- data/lib/aws-sdk-s3/client_api.rb +1296 -197
- data/lib/aws-sdk-s3/customizations/bucket.rb +56 -37
- data/lib/aws-sdk-s3/customizations/errors.rb +40 -0
- data/lib/aws-sdk-s3/customizations/multipart_upload.rb +2 -0
- data/lib/aws-sdk-s3/customizations/object.rb +288 -68
- data/lib/aws-sdk-s3/customizations/object_summary.rb +10 -0
- data/lib/aws-sdk-s3/customizations/object_version.rb +13 -0
- data/lib/aws-sdk-s3/customizations/types/list_object_versions_output.rb +2 -0
- data/lib/aws-sdk-s3/customizations/types/permanent_redirect.rb +26 -0
- data/lib/aws-sdk-s3/customizations.rb +27 -28
- data/lib/aws-sdk-s3/encryption/client.rb +28 -7
- data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +71 -29
- data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +43 -5
- data/lib/aws-sdk-s3/encryption/default_key_provider.rb +2 -0
- data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +13 -2
- data/lib/aws-sdk-s3/encryption/errors.rb +2 -0
- data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +2 -0
- data/lib/aws-sdk-s3/encryption/io_decrypter.rb +11 -3
- data/lib/aws-sdk-s3/encryption/io_encrypter.rb +2 -0
- data/lib/aws-sdk-s3/encryption/key_provider.rb +2 -0
- data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +46 -11
- data/lib/aws-sdk-s3/encryption/materials.rb +8 -6
- data/lib/aws-sdk-s3/encryption/utils.rb +25 -0
- data/lib/aws-sdk-s3/encryption.rb +4 -0
- data/lib/aws-sdk-s3/encryptionV2/client.rb +570 -0
- data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +223 -0
- data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +170 -0
- data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +40 -0
- data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +65 -0
- data/lib/aws-sdk-s3/encryptionV2/errors.rb +37 -0
- data/lib/aws-sdk-s3/encryptionV2/io_auth_decrypter.rb +58 -0
- data/lib/aws-sdk-s3/encryptionV2/io_decrypter.rb +37 -0
- data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +73 -0
- data/lib/aws-sdk-s3/encryptionV2/key_provider.rb +31 -0
- data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +173 -0
- data/lib/aws-sdk-s3/encryptionV2/materials.rb +60 -0
- data/lib/aws-sdk-s3/encryptionV2/utils.rb +103 -0
- data/lib/aws-sdk-s3/encryption_v2.rb +23 -0
- data/lib/aws-sdk-s3/endpoint_parameters.rb +181 -0
- data/lib/aws-sdk-s3/endpoint_provider.rb +716 -0
- data/lib/aws-sdk-s3/endpoints.rb +1434 -0
- data/lib/aws-sdk-s3/errors.rb +170 -1
- data/lib/aws-sdk-s3/event_streams.rb +8 -1
- data/lib/aws-sdk-s3/express_credentials.rb +55 -0
- data/lib/aws-sdk-s3/express_credentials_provider.rb +59 -0
- data/lib/aws-sdk-s3/file_downloader.rb +161 -46
- data/lib/aws-sdk-s3/file_part.rb +11 -6
- data/lib/aws-sdk-s3/file_uploader.rb +39 -18
- data/lib/aws-sdk-s3/legacy_signer.rb +17 -25
- data/lib/aws-sdk-s3/multipart_file_uploader.rb +104 -27
- data/lib/aws-sdk-s3/multipart_stream_uploader.rb +61 -21
- data/lib/aws-sdk-s3/multipart_upload.rb +342 -32
- data/lib/aws-sdk-s3/multipart_upload_error.rb +2 -0
- data/lib/aws-sdk-s3/multipart_upload_part.rb +384 -46
- data/lib/aws-sdk-s3/object.rb +2600 -231
- data/lib/aws-sdk-s3/object_acl.rb +103 -25
- data/lib/aws-sdk-s3/object_copier.rb +9 -5
- data/lib/aws-sdk-s3/object_multipart_copier.rb +48 -22
- data/lib/aws-sdk-s3/object_summary.rb +2174 -204
- data/lib/aws-sdk-s3/object_version.rb +539 -80
- data/lib/aws-sdk-s3/plugins/accelerate.rb +17 -64
- data/lib/aws-sdk-s3/plugins/access_grants.rb +178 -0
- data/lib/aws-sdk-s3/plugins/arn.rb +70 -0
- data/lib/aws-sdk-s3/plugins/bucket_dns.rb +7 -43
- data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +20 -3
- data/lib/aws-sdk-s3/plugins/checksum_algorithm.rb +31 -0
- data/lib/aws-sdk-s3/plugins/dualstack.rb +7 -50
- data/lib/aws-sdk-s3/plugins/endpoints.rb +86 -0
- data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +5 -4
- data/lib/aws-sdk-s3/plugins/express_session_auth.rb +88 -0
- data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +3 -1
- data/lib/aws-sdk-s3/plugins/http_200_errors.rb +62 -17
- data/lib/aws-sdk-s3/plugins/iad_regional_endpoint.rb +44 -0
- data/lib/aws-sdk-s3/plugins/location_constraint.rb +5 -1
- data/lib/aws-sdk-s3/plugins/md5s.rb +14 -70
- data/lib/aws-sdk-s3/plugins/redirects.rb +2 -0
- data/lib/aws-sdk-s3/plugins/s3_host_id.rb +2 -0
- data/lib/aws-sdk-s3/plugins/s3_signer.rb +63 -94
- data/lib/aws-sdk-s3/plugins/sse_cpk.rb +3 -1
- data/lib/aws-sdk-s3/plugins/streaming_retry.rb +139 -0
- data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +2 -0
- data/lib/aws-sdk-s3/presigned_post.rb +160 -99
- data/lib/aws-sdk-s3/presigner.rb +141 -62
- data/lib/aws-sdk-s3/resource.rb +156 -17
- data/lib/aws-sdk-s3/types.rb +13021 -4106
- data/lib/aws-sdk-s3/waiters.rb +67 -1
- data/lib/aws-sdk-s3.rb +46 -32
- data/sig/bucket.rbs +222 -0
- data/sig/bucket_acl.rbs +78 -0
- data/sig/bucket_cors.rbs +69 -0
- data/sig/bucket_lifecycle.rbs +88 -0
- data/sig/bucket_lifecycle_configuration.rbs +115 -0
- data/sig/bucket_logging.rbs +76 -0
- data/sig/bucket_notification.rbs +114 -0
- data/sig/bucket_policy.rbs +59 -0
- data/sig/bucket_request_payment.rbs +54 -0
- data/sig/bucket_tagging.rbs +65 -0
- data/sig/bucket_versioning.rbs +77 -0
- data/sig/bucket_website.rbs +93 -0
- data/sig/client.rbs +2472 -0
- data/sig/customizations/bucket.rbs +19 -0
- data/sig/customizations/object.rbs +38 -0
- data/sig/customizations/object_summary.rbs +35 -0
- data/sig/errors.rbs +42 -0
- data/sig/multipart_upload.rbs +120 -0
- data/sig/multipart_upload_part.rbs +109 -0
- data/sig/object.rbs +459 -0
- data/sig/object_acl.rbs +86 -0
- data/sig/object_summary.rbs +345 -0
- data/sig/object_version.rbs +143 -0
- data/sig/resource.rbs +134 -0
- data/sig/types.rbs +2712 -0
- data/sig/waiters.rbs +95 -0
- metadata +74 -15
data/lib/aws-sdk-s3/errors.rb
CHANGED
@@ -1,14 +1,183 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# WARNING ABOUT GENERATED CODE
|
2
4
|
#
|
3
5
|
# This file is generated. See the contributing guide for more information:
|
4
|
-
# https://github.com/aws/aws-sdk-ruby/blob/
|
6
|
+
# https://github.com/aws/aws-sdk-ruby/blob/version-3/CONTRIBUTING.md
|
5
7
|
#
|
6
8
|
# WARNING ABOUT GENERATED CODE
|
7
9
|
|
8
10
|
module Aws::S3
|
11
|
+
|
12
|
+
# When S3 returns an error response, the Ruby SDK constructs and raises an error.
|
13
|
+
# These errors all extend Aws::S3::Errors::ServiceError < {Aws::Errors::ServiceError}
|
14
|
+
#
|
15
|
+
# You can rescue all S3 errors using ServiceError:
|
16
|
+
#
|
17
|
+
# begin
|
18
|
+
# # do stuff
|
19
|
+
# rescue Aws::S3::Errors::ServiceError
|
20
|
+
# # rescues all S3 API errors
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
#
|
24
|
+
# ## Request Context
|
25
|
+
# ServiceError objects have a {Aws::Errors::ServiceError#context #context} method that returns
|
26
|
+
# information about the request that generated the error.
|
27
|
+
# See {Seahorse::Client::RequestContext} for more information.
|
28
|
+
#
|
29
|
+
# ## Error Classes
|
30
|
+
# * {BucketAlreadyExists}
|
31
|
+
# * {BucketAlreadyOwnedByYou}
|
32
|
+
# * {EncryptionTypeMismatch}
|
33
|
+
# * {InvalidObjectState}
|
34
|
+
# * {InvalidRequest}
|
35
|
+
# * {InvalidWriteOffset}
|
36
|
+
# * {NoSuchBucket}
|
37
|
+
# * {NoSuchKey}
|
38
|
+
# * {NoSuchUpload}
|
39
|
+
# * {ObjectAlreadyInActiveTierError}
|
40
|
+
# * {ObjectNotInActiveTierError}
|
41
|
+
# * {TooManyParts}
|
42
|
+
#
|
43
|
+
# Additionally, error classes are dynamically generated for service errors based on the error code
|
44
|
+
# if they are not defined above.
|
9
45
|
module Errors
|
10
46
|
|
11
47
|
extend Aws::Errors::DynamicErrors
|
12
48
|
|
49
|
+
class BucketAlreadyExists < ServiceError
|
50
|
+
|
51
|
+
# @param [Seahorse::Client::RequestContext] context
|
52
|
+
# @param [String] message
|
53
|
+
# @param [Aws::S3::Types::BucketAlreadyExists] data
|
54
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
55
|
+
super(context, message, data)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class BucketAlreadyOwnedByYou < ServiceError
|
60
|
+
|
61
|
+
# @param [Seahorse::Client::RequestContext] context
|
62
|
+
# @param [String] message
|
63
|
+
# @param [Aws::S3::Types::BucketAlreadyOwnedByYou] data
|
64
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
65
|
+
super(context, message, data)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class EncryptionTypeMismatch < ServiceError
|
70
|
+
|
71
|
+
# @param [Seahorse::Client::RequestContext] context
|
72
|
+
# @param [String] message
|
73
|
+
# @param [Aws::S3::Types::EncryptionTypeMismatch] data
|
74
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
75
|
+
super(context, message, data)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class InvalidObjectState < ServiceError
|
80
|
+
|
81
|
+
# @param [Seahorse::Client::RequestContext] context
|
82
|
+
# @param [String] message
|
83
|
+
# @param [Aws::S3::Types::InvalidObjectState] data
|
84
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
85
|
+
super(context, message, data)
|
86
|
+
end
|
87
|
+
|
88
|
+
# @return [String]
|
89
|
+
def storage_class
|
90
|
+
@data[:storage_class]
|
91
|
+
end
|
92
|
+
|
93
|
+
# @return [String]
|
94
|
+
def access_tier
|
95
|
+
@data[:access_tier]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class InvalidRequest < ServiceError
|
100
|
+
|
101
|
+
# @param [Seahorse::Client::RequestContext] context
|
102
|
+
# @param [String] message
|
103
|
+
# @param [Aws::S3::Types::InvalidRequest] data
|
104
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
105
|
+
super(context, message, data)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class InvalidWriteOffset < ServiceError
|
110
|
+
|
111
|
+
# @param [Seahorse::Client::RequestContext] context
|
112
|
+
# @param [String] message
|
113
|
+
# @param [Aws::S3::Types::InvalidWriteOffset] data
|
114
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
115
|
+
super(context, message, data)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class NoSuchBucket < ServiceError
|
120
|
+
|
121
|
+
# @param [Seahorse::Client::RequestContext] context
|
122
|
+
# @param [String] message
|
123
|
+
# @param [Aws::S3::Types::NoSuchBucket] data
|
124
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
125
|
+
super(context, message, data)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class NoSuchKey < ServiceError
|
130
|
+
|
131
|
+
# @param [Seahorse::Client::RequestContext] context
|
132
|
+
# @param [String] message
|
133
|
+
# @param [Aws::S3::Types::NoSuchKey] data
|
134
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
135
|
+
super(context, message, data)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class NoSuchUpload < ServiceError
|
140
|
+
|
141
|
+
# @param [Seahorse::Client::RequestContext] context
|
142
|
+
# @param [String] message
|
143
|
+
# @param [Aws::S3::Types::NoSuchUpload] data
|
144
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
145
|
+
super(context, message, data)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class ObjectAlreadyInActiveTierError < ServiceError
|
150
|
+
|
151
|
+
# @param [Seahorse::Client::RequestContext] context
|
152
|
+
# @param [String] message
|
153
|
+
# @param [Aws::S3::Types::ObjectAlreadyInActiveTierError] data
|
154
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
155
|
+
super(context, message, data)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
class ObjectNotInActiveTierError < ServiceError
|
160
|
+
|
161
|
+
# @param [Seahorse::Client::RequestContext] context
|
162
|
+
# @param [String] message
|
163
|
+
# @param [Aws::S3::Types::ObjectNotInActiveTierError] data
|
164
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
165
|
+
super(context, message, data)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
class TooManyParts < ServiceError
|
170
|
+
|
171
|
+
# @param [Seahorse::Client::RequestContext] context
|
172
|
+
# @param [String] message
|
173
|
+
# @param [Aws::S3::Types::TooManyParts] data
|
174
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
175
|
+
super(context, message, data)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
13
179
|
end
|
14
180
|
end
|
181
|
+
|
182
|
+
# Load customizations if they exist
|
183
|
+
require 'aws-sdk-s3/customizations/errors'
|
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# WARNING ABOUT GENERATED CODE
|
2
4
|
#
|
3
5
|
# This file is generated. See the contributing guide for more information:
|
4
|
-
# https://github.com/aws/aws-sdk-ruby/blob/
|
6
|
+
# https://github.com/aws/aws-sdk-ruby/blob/version-3/CONTRIBUTING.md
|
5
7
|
#
|
6
8
|
# WARNING ABOUT GENERATED CODE
|
7
9
|
|
@@ -41,6 +43,10 @@ module Aws::S3
|
|
41
43
|
@event_emitter.on(:initial_response, block) if block_given?
|
42
44
|
end
|
43
45
|
|
46
|
+
def on_unknown_event(&block)
|
47
|
+
@event_emitter.on(:unknown_event, block) if block_given?
|
48
|
+
end
|
49
|
+
|
44
50
|
def on_event(&block)
|
45
51
|
on_records_event(&block)
|
46
52
|
on_stats_event(&block)
|
@@ -49,6 +55,7 @@ module Aws::S3
|
|
49
55
|
on_end_event(&block)
|
50
56
|
on_error_event(&block)
|
51
57
|
on_initial_response_event(&block)
|
58
|
+
on_unknown_event(&block)
|
52
59
|
end
|
53
60
|
|
54
61
|
# @api private
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module Aws
|
6
|
+
module S3
|
7
|
+
# @api private
|
8
|
+
class ExpressCredentials
|
9
|
+
include CredentialProvider
|
10
|
+
include RefreshingCredentials
|
11
|
+
|
12
|
+
SYNC_EXPIRATION_LENGTH = 60 # 1 minute
|
13
|
+
ASYNC_EXPIRATION_LENGTH = 120 # 2 minutes
|
14
|
+
|
15
|
+
def initialize(options = {})
|
16
|
+
@client = options[:client]
|
17
|
+
@create_session_params = {}
|
18
|
+
options.each_pair do |key, value|
|
19
|
+
if self.class.create_session_options.include?(key)
|
20
|
+
@create_session_params[key] = value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
@async_refresh = true
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [S3::Client]
|
28
|
+
attr_reader :client
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def refresh
|
33
|
+
c = @client.create_session(@create_session_params).credentials
|
34
|
+
@credentials = Credentials.new(
|
35
|
+
c.access_key_id,
|
36
|
+
c.secret_access_key,
|
37
|
+
c.session_token
|
38
|
+
)
|
39
|
+
@expiration = c.expiration
|
40
|
+
end
|
41
|
+
|
42
|
+
class << self
|
43
|
+
|
44
|
+
# @api private
|
45
|
+
def create_session_options
|
46
|
+
@cso ||= begin
|
47
|
+
input = S3::Client.api.operation(:create_session).input
|
48
|
+
Set.new(input.shape.member_names)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aws
|
4
|
+
module S3
|
5
|
+
# @api private
|
6
|
+
def self.express_credentials_cache
|
7
|
+
@express_credentials_cache ||= LRUCache.new(max_entries: 100)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns Credentials class for S3 Express. Accepts CreateSession
|
11
|
+
# params as options. See {Client#create_session} for details.
|
12
|
+
class ExpressCredentialsProvider
|
13
|
+
# @param [Hash] options
|
14
|
+
# @option options [Client] :client The S3 client used to create the
|
15
|
+
# session.
|
16
|
+
# @option options [String] :session_mode (see: {Client#create_session})
|
17
|
+
# @option options [Boolean] :caching (true) When true, credentials will
|
18
|
+
# be cached.
|
19
|
+
# @option options [Callable] :before_refresh Proc called before
|
20
|
+
# credentials are refreshed.
|
21
|
+
def initialize(options = {})
|
22
|
+
@client = options.delete(:client)
|
23
|
+
@caching = options.delete(:caching) != false
|
24
|
+
@options = options
|
25
|
+
return unless @caching
|
26
|
+
|
27
|
+
@cache = Aws::S3.express_credentials_cache
|
28
|
+
end
|
29
|
+
|
30
|
+
def express_credentials_for(bucket)
|
31
|
+
if @caching
|
32
|
+
cached_credentials_for(bucket)
|
33
|
+
else
|
34
|
+
new_credentials_for(bucket)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_accessor :client
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def cached_credentials_for(bucket)
|
43
|
+
if @cache.key?(bucket)
|
44
|
+
@cache[bucket]
|
45
|
+
else
|
46
|
+
@cache[bucket] = new_credentials_for(bucket)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def new_credentials_for(bucket)
|
51
|
+
ExpressCredentials.new(
|
52
|
+
bucket: bucket,
|
53
|
+
client: @client,
|
54
|
+
**@options
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'pathname'
|
2
4
|
require 'thread'
|
3
5
|
require 'set'
|
@@ -21,48 +23,64 @@ module Aws
|
|
21
23
|
|
22
24
|
def download(destination, options = {})
|
23
25
|
@path = destination
|
24
|
-
@mode = options[:mode] ||
|
26
|
+
@mode = options[:mode] || 'auto'
|
25
27
|
@thread_count = options[:thread_count] || THREAD_COUNT
|
26
28
|
@chunk_size = options[:chunk_size]
|
27
29
|
@params = {
|
28
30
|
bucket: options[:bucket],
|
29
|
-
key: options[:key]
|
31
|
+
key: options[:key]
|
30
32
|
}
|
31
33
|
@params[:version_id] = options[:version_id] if options[:version_id]
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
34
|
+
@on_checksum_validated = options[:on_checksum_validated]
|
35
|
+
@progress_callback = options[:progress_callback]
|
36
|
+
|
37
|
+
validate!
|
38
|
+
|
39
|
+
Aws::Plugins::UserAgent.metric('S3_TRANSFER') do
|
40
|
+
case @mode
|
41
|
+
when 'auto' then multipart_download
|
42
|
+
when 'single_request' then single_request
|
43
|
+
when 'get_range'
|
44
|
+
if @chunk_size
|
45
|
+
resp = @client.head_object(@params)
|
46
|
+
multithreaded_get_by_ranges(resp.content_length)
|
47
|
+
else
|
48
|
+
msg = 'In :get_range mode, :chunk_size must be provided'
|
49
|
+
raise ArgumentError, msg
|
50
|
+
end
|
40
51
|
else
|
41
|
-
msg = "
|
52
|
+
msg = "Invalid mode #{@mode} provided, "\
|
53
|
+
'mode should be :single_request, :get_range or :auto'
|
42
54
|
raise ArgumentError, msg
|
43
55
|
end
|
44
|
-
else
|
45
|
-
msg = "Invalid mode #{@mode} provided, "\
|
46
|
-
"mode should be :single_request, :get_range or :auto"
|
47
|
-
raise ArgumentError, msg
|
48
56
|
end
|
49
57
|
end
|
50
58
|
|
51
59
|
private
|
52
60
|
|
61
|
+
def validate!
|
62
|
+
if @on_checksum_validated && !@on_checksum_validated.respond_to?(:call)
|
63
|
+
raise ArgumentError, 'on_checksum_validated must be callable'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
53
67
|
def multipart_download
|
54
68
|
resp = @client.head_object(@params.merge(part_number: 1))
|
55
69
|
count = resp.parts_count
|
56
70
|
if count.nil? || count <= 1
|
57
|
-
resp.content_length
|
58
|
-
single_request
|
59
|
-
|
71
|
+
if resp.content_length <= MIN_CHUNK_SIZE
|
72
|
+
single_request
|
73
|
+
else
|
74
|
+
multithreaded_get_by_ranges(resp.content_length)
|
75
|
+
end
|
60
76
|
else
|
61
77
|
# partNumber is an option
|
62
78
|
resp = @client.head_object(@params)
|
63
|
-
resp.content_length
|
64
|
-
single_request
|
79
|
+
if resp.content_length <= MIN_CHUNK_SIZE
|
80
|
+
single_request
|
81
|
+
else
|
65
82
|
compute_mode(resp.content_length, count)
|
83
|
+
end
|
66
84
|
end
|
67
85
|
end
|
68
86
|
|
@@ -70,9 +88,9 @@ module Aws
|
|
70
88
|
chunk_size = compute_chunk(file_size)
|
71
89
|
part_size = (file_size.to_f / count.to_f).ceil
|
72
90
|
if chunk_size < part_size
|
73
|
-
multithreaded_get_by_ranges(
|
91
|
+
multithreaded_get_by_ranges(file_size)
|
74
92
|
else
|
75
|
-
multithreaded_get_by_parts(count)
|
93
|
+
multithreaded_get_by_parts(count, file_size)
|
76
94
|
end
|
77
95
|
end
|
78
96
|
|
@@ -80,10 +98,11 @@ module Aws
|
|
80
98
|
offset = 0
|
81
99
|
default_chunk_size = compute_chunk(file_size)
|
82
100
|
chunks = []
|
83
|
-
while offset
|
101
|
+
while offset < file_size
|
84
102
|
progress = offset + default_chunk_size
|
85
|
-
|
86
|
-
|
103
|
+
progress = file_size if progress > file_size
|
104
|
+
chunks << "bytes=#{offset}-#{progress - 1}"
|
105
|
+
offset = progress
|
87
106
|
end
|
88
107
|
chunks
|
89
108
|
end
|
@@ -92,7 +111,9 @@ module Aws
|
|
92
111
|
if @chunk_size && @chunk_size > file_size
|
93
112
|
raise ArgumentError, ":chunk_size shouldn't exceed total file size."
|
94
113
|
else
|
95
|
-
@chunk_size || [
|
114
|
+
@chunk_size || [
|
115
|
+
(file_size.to_f / MAX_PARTS).ceil, MIN_CHUNK_SIZE
|
116
|
+
].max.to_i
|
96
117
|
end
|
97
118
|
end
|
98
119
|
|
@@ -101,39 +122,133 @@ module Aws
|
|
101
122
|
chunks.each_slice(@thread_count).to_a
|
102
123
|
end
|
103
124
|
|
104
|
-
def multithreaded_get_by_ranges(
|
105
|
-
|
125
|
+
def multithreaded_get_by_ranges(file_size)
|
126
|
+
offset = 0
|
127
|
+
default_chunk_size = compute_chunk(file_size)
|
128
|
+
chunks = []
|
129
|
+
part_number = 1 # parts start at 1
|
130
|
+
while offset < file_size
|
131
|
+
progress = offset + default_chunk_size
|
132
|
+
progress = file_size if progress > file_size
|
133
|
+
range = "bytes=#{offset}-#{progress - 1}"
|
134
|
+
chunks << Part.new(
|
135
|
+
part_number: part_number,
|
136
|
+
size: (progress-offset),
|
137
|
+
params: @params.merge(range: range)
|
138
|
+
)
|
139
|
+
part_number += 1
|
140
|
+
offset = progress
|
141
|
+
end
|
142
|
+
download_in_threads(PartList.new(chunks), file_size)
|
106
143
|
end
|
107
144
|
|
108
|
-
def multithreaded_get_by_parts(
|
109
|
-
|
145
|
+
def multithreaded_get_by_parts(n_parts, total_size)
|
146
|
+
parts = (1..n_parts).map do |part|
|
147
|
+
Part.new(part_number: part, params: @params.merge(part_number: part))
|
148
|
+
end
|
149
|
+
download_in_threads(PartList.new(parts), total_size)
|
110
150
|
end
|
111
151
|
|
112
|
-
def
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
152
|
+
def download_in_threads(pending, total_size)
|
153
|
+
threads = []
|
154
|
+
progress = MultipartProgress.new(pending, total_size, @progress_callback) if @progress_callback
|
155
|
+
@thread_count.times do
|
156
|
+
thread = Thread.new do
|
157
|
+
begin
|
158
|
+
while part = pending.shift
|
159
|
+
if progress
|
160
|
+
part.params[:on_chunk_received] =
|
161
|
+
proc do |_chunk, bytes, total|
|
162
|
+
progress.call(part.part_number, bytes, total)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
resp = @client.get_object(part.params)
|
166
|
+
write(resp)
|
167
|
+
if @on_checksum_validated && resp.checksum_validated
|
168
|
+
@on_checksum_validated.call(resp.checksum_validated, resp)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
nil
|
172
|
+
rescue => error
|
173
|
+
# keep other threads from downloading other parts
|
174
|
+
pending.clear!
|
175
|
+
raise error
|
121
176
|
end
|
122
177
|
end
|
123
|
-
threads
|
178
|
+
threads << thread
|
124
179
|
end
|
180
|
+
threads.map(&:value).compact
|
125
181
|
end
|
126
182
|
|
127
183
|
def write(resp)
|
128
|
-
range, _ = resp.content_range.split(
|
129
|
-
head, _ = range.split(
|
130
|
-
|
184
|
+
range, _ = resp.content_range.split(' ').last.split('/')
|
185
|
+
head, _ = range.split('-').map {|s| s.to_i}
|
186
|
+
File.write(@path, resp.body.read, head)
|
131
187
|
end
|
132
188
|
|
133
189
|
def single_request
|
134
|
-
@
|
135
|
-
|
136
|
-
)
|
190
|
+
params = @params.merge(response_target: @path)
|
191
|
+
params[:on_chunk_received] = single_part_progress if @progress_callback
|
192
|
+
resp = @client.get_object(params)
|
193
|
+
|
194
|
+
return resp unless @on_checksum_validated
|
195
|
+
|
196
|
+
@on_checksum_validated.call(resp.checksum_validated, resp) if resp.checksum_validated
|
197
|
+
|
198
|
+
resp
|
199
|
+
end
|
200
|
+
|
201
|
+
def single_part_progress
|
202
|
+
proc do |_chunk, bytes_read, total_size|
|
203
|
+
@progress_callback.call([bytes_read], [total_size], total_size)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
class Part < Struct.new(:part_number, :size, :params)
|
208
|
+
include Aws::Structure
|
209
|
+
end
|
210
|
+
|
211
|
+
# @api private
|
212
|
+
class PartList
|
213
|
+
include Enumerable
|
214
|
+
def initialize(parts = [])
|
215
|
+
@parts = parts
|
216
|
+
@mutex = Mutex.new
|
217
|
+
end
|
218
|
+
|
219
|
+
def shift
|
220
|
+
@mutex.synchronize { @parts.shift }
|
221
|
+
end
|
222
|
+
|
223
|
+
def size
|
224
|
+
@mutex.synchronize { @parts.size }
|
225
|
+
end
|
226
|
+
|
227
|
+
def clear!
|
228
|
+
@mutex.synchronize { @parts.clear }
|
229
|
+
end
|
230
|
+
|
231
|
+
def each(&block)
|
232
|
+
@mutex.synchronize { @parts.each(&block) }
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# @api private
|
237
|
+
class MultipartProgress
|
238
|
+
def initialize(parts, total_size, progress_callback)
|
239
|
+
@bytes_received = Array.new(parts.size, 0)
|
240
|
+
@part_sizes = parts.map(&:size)
|
241
|
+
@total_size = total_size
|
242
|
+
@progress_callback = progress_callback
|
243
|
+
end
|
244
|
+
|
245
|
+
def call(part_number, bytes_received, total)
|
246
|
+
# part numbers start at 1
|
247
|
+
@bytes_received[part_number - 1] = bytes_received
|
248
|
+
# part size may not be known until we get the first response
|
249
|
+
@part_sizes[part_number - 1] ||= total
|
250
|
+
@progress_callback.call(@bytes_received, @part_sizes, @total_size)
|
251
|
+
end
|
137
252
|
end
|
138
253
|
end
|
139
254
|
end
|
data/lib/aws-sdk-s3/file_part.rb
CHANGED
@@ -1,15 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Aws
|
2
4
|
module S3
|
3
5
|
|
4
|
-
# A utility class that provides an IO-like interface to a portion of
|
5
|
-
#
|
6
|
+
# A utility class that provides an IO-like interface to a portion of a file
|
7
|
+
# on disk.
|
6
8
|
# @api private
|
7
9
|
class FilePart
|
8
10
|
|
9
|
-
# @option options [required,String,Pathname,File,Tempfile] :source
|
10
|
-
#
|
11
|
+
# @option options [required, String, Pathname, File, Tempfile] :source
|
12
|
+
# The file to upload.
|
13
|
+
#
|
14
|
+
# @option options [required, Integer] :offset The file part will read
|
11
15
|
# starting at this byte offset.
|
12
|
-
#
|
16
|
+
#
|
17
|
+
# @option options [required, Integer] :size The maximum number of bytes to
|
13
18
|
# read from the `:offset`.
|
14
19
|
def initialize(options = {})
|
15
20
|
@source = options[:source]
|
@@ -19,7 +24,7 @@ module Aws
|
|
19
24
|
@file = nil
|
20
25
|
end
|
21
26
|
|
22
|
-
# @return [String,Pathname,File,Tempfile]
|
27
|
+
# @return [String, Pathname, File, Tempfile]
|
23
28
|
attr_reader :source
|
24
29
|
|
25
30
|
# @return [Integer]
|