aws-sdk-s3 1.75.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/lib/aws-sdk-s3.rb +73 -0
  3. data/lib/aws-sdk-s3/bucket.rb +861 -0
  4. data/lib/aws-sdk-s3/bucket_acl.rb +277 -0
  5. data/lib/aws-sdk-s3/bucket_cors.rb +262 -0
  6. data/lib/aws-sdk-s3/bucket_lifecycle.rb +264 -0
  7. data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +283 -0
  8. data/lib/aws-sdk-s3/bucket_logging.rb +251 -0
  9. data/lib/aws-sdk-s3/bucket_notification.rb +293 -0
  10. data/lib/aws-sdk-s3/bucket_policy.rb +242 -0
  11. data/lib/aws-sdk-s3/bucket_region_cache.rb +81 -0
  12. data/lib/aws-sdk-s3/bucket_request_payment.rb +236 -0
  13. data/lib/aws-sdk-s3/bucket_tagging.rb +251 -0
  14. data/lib/aws-sdk-s3/bucket_versioning.rb +312 -0
  15. data/lib/aws-sdk-s3/bucket_website.rb +292 -0
  16. data/lib/aws-sdk-s3/client.rb +11818 -0
  17. data/lib/aws-sdk-s3/client_api.rb +3014 -0
  18. data/lib/aws-sdk-s3/customizations.rb +34 -0
  19. data/lib/aws-sdk-s3/customizations/bucket.rb +162 -0
  20. data/lib/aws-sdk-s3/customizations/multipart_upload.rb +44 -0
  21. data/lib/aws-sdk-s3/customizations/object.rb +389 -0
  22. data/lib/aws-sdk-s3/customizations/object_summary.rb +85 -0
  23. data/lib/aws-sdk-s3/customizations/types/list_object_versions_output.rb +13 -0
  24. data/lib/aws-sdk-s3/encryption.rb +21 -0
  25. data/lib/aws-sdk-s3/encryption/client.rb +375 -0
  26. data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +190 -0
  27. data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +65 -0
  28. data/lib/aws-sdk-s3/encryption/default_key_provider.rb +40 -0
  29. data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +61 -0
  30. data/lib/aws-sdk-s3/encryption/errors.rb +15 -0
  31. data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +58 -0
  32. data/lib/aws-sdk-s3/encryption/io_decrypter.rb +36 -0
  33. data/lib/aws-sdk-s3/encryption/io_encrypter.rb +71 -0
  34. data/lib/aws-sdk-s3/encryption/key_provider.rb +31 -0
  35. data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +75 -0
  36. data/lib/aws-sdk-s3/encryption/materials.rb +60 -0
  37. data/lib/aws-sdk-s3/encryption/utils.rb +81 -0
  38. data/lib/aws-sdk-s3/encryptionV2/client.rb +388 -0
  39. data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +198 -0
  40. data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +103 -0
  41. data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +38 -0
  42. data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +66 -0
  43. data/lib/aws-sdk-s3/encryptionV2/errors.rb +13 -0
  44. data/lib/aws-sdk-s3/encryptionV2/io_auth_decrypter.rb +56 -0
  45. data/lib/aws-sdk-s3/encryptionV2/io_decrypter.rb +35 -0
  46. data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +71 -0
  47. data/lib/aws-sdk-s3/encryptionV2/key_provider.rb +29 -0
  48. data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +99 -0
  49. data/lib/aws-sdk-s3/encryptionV2/materials.rb +58 -0
  50. data/lib/aws-sdk-s3/encryptionV2/utils.rb +116 -0
  51. data/lib/aws-sdk-s3/encryption_v2.rb +20 -0
  52. data/lib/aws-sdk-s3/errors.rb +115 -0
  53. data/lib/aws-sdk-s3/event_streams.rb +69 -0
  54. data/lib/aws-sdk-s3/file_downloader.rb +142 -0
  55. data/lib/aws-sdk-s3/file_part.rb +78 -0
  56. data/lib/aws-sdk-s3/file_uploader.rb +70 -0
  57. data/lib/aws-sdk-s3/legacy_signer.rb +189 -0
  58. data/lib/aws-sdk-s3/multipart_file_uploader.rb +227 -0
  59. data/lib/aws-sdk-s3/multipart_stream_uploader.rb +173 -0
  60. data/lib/aws-sdk-s3/multipart_upload.rb +401 -0
  61. data/lib/aws-sdk-s3/multipart_upload_error.rb +18 -0
  62. data/lib/aws-sdk-s3/multipart_upload_part.rb +423 -0
  63. data/lib/aws-sdk-s3/object.rb +1422 -0
  64. data/lib/aws-sdk-s3/object_acl.rb +333 -0
  65. data/lib/aws-sdk-s3/object_copier.rb +101 -0
  66. data/lib/aws-sdk-s3/object_multipart_copier.rb +182 -0
  67. data/lib/aws-sdk-s3/object_summary.rb +1181 -0
  68. data/lib/aws-sdk-s3/object_version.rb +550 -0
  69. data/lib/aws-sdk-s3/plugins/accelerate.rb +87 -0
  70. data/lib/aws-sdk-s3/plugins/bucket_arn.rb +212 -0
  71. data/lib/aws-sdk-s3/plugins/bucket_dns.rb +91 -0
  72. data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +45 -0
  73. data/lib/aws-sdk-s3/plugins/dualstack.rb +74 -0
  74. data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +28 -0
  75. data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +25 -0
  76. data/lib/aws-sdk-s3/plugins/http_200_errors.rb +55 -0
  77. data/lib/aws-sdk-s3/plugins/iad_regional_endpoint.rb +62 -0
  78. data/lib/aws-sdk-s3/plugins/location_constraint.rb +35 -0
  79. data/lib/aws-sdk-s3/plugins/md5s.rb +84 -0
  80. data/lib/aws-sdk-s3/plugins/redirects.rb +45 -0
  81. data/lib/aws-sdk-s3/plugins/s3_host_id.rb +30 -0
  82. data/lib/aws-sdk-s3/plugins/s3_signer.rb +222 -0
  83. data/lib/aws-sdk-s3/plugins/sse_cpk.rb +70 -0
  84. data/lib/aws-sdk-s3/plugins/streaming_retry.rb +118 -0
  85. data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +97 -0
  86. data/lib/aws-sdk-s3/presigned_post.rb +686 -0
  87. data/lib/aws-sdk-s3/presigner.rb +253 -0
  88. data/lib/aws-sdk-s3/resource.rb +117 -0
  89. data/lib/aws-sdk-s3/types.rb +13154 -0
  90. data/lib/aws-sdk-s3/waiters.rb +243 -0
  91. metadata +184 -0
@@ -0,0 +1,333 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WARNING ABOUT GENERATED CODE
4
+ #
5
+ # This file is generated. See the contributing guide for more information:
6
+ # https://github.com/aws/aws-sdk-ruby/blob/master/CONTRIBUTING.md
7
+ #
8
+ # WARNING ABOUT GENERATED CODE
9
+
10
+ module Aws::S3
11
+
12
+ class ObjectAcl
13
+
14
+ extend Aws::Deprecations
15
+
16
+ # @overload def initialize(bucket_name, object_key, options = {})
17
+ # @param [String] bucket_name
18
+ # @param [String] object_key
19
+ # @option options [Client] :client
20
+ # @overload def initialize(options = {})
21
+ # @option options [required, String] :bucket_name
22
+ # @option options [required, String] :object_key
23
+ # @option options [Client] :client
24
+ def initialize(*args)
25
+ options = Hash === args.last ? args.pop.dup : {}
26
+ @bucket_name = extract_bucket_name(args, options)
27
+ @object_key = extract_object_key(args, options)
28
+ @data = options.delete(:data)
29
+ @client = options.delete(:client) || Client.new(options)
30
+ @waiter_block_warned = false
31
+ end
32
+
33
+ # @!group Read-Only Attributes
34
+
35
+ # @return [String]
36
+ def bucket_name
37
+ @bucket_name
38
+ end
39
+
40
+ # @return [String]
41
+ def object_key
42
+ @object_key
43
+ end
44
+
45
+ # Container for the bucket owner's display name and ID.
46
+ # @return [Types::Owner]
47
+ def owner
48
+ data[:owner]
49
+ end
50
+
51
+ # A list of grants.
52
+ # @return [Array<Types::Grant>]
53
+ def grants
54
+ data[:grants]
55
+ end
56
+
57
+ # If present, indicates that the requester was successfully charged for
58
+ # the request.
59
+ # @return [String]
60
+ def request_charged
61
+ data[:request_charged]
62
+ end
63
+
64
+ # @!endgroup
65
+
66
+ # @return [Client]
67
+ def client
68
+ @client
69
+ end
70
+
71
+ # Loads, or reloads {#data} for the current {ObjectAcl}.
72
+ # Returns `self` making it possible to chain methods.
73
+ #
74
+ # object_acl.reload.data
75
+ #
76
+ # @return [self]
77
+ def load
78
+ resp = @client.get_object_acl(
79
+ bucket: @bucket_name,
80
+ key: @object_key
81
+ )
82
+ @data = resp.data
83
+ self
84
+ end
85
+ alias :reload :load
86
+
87
+ # @return [Types::GetObjectAclOutput]
88
+ # Returns the data for this {ObjectAcl}. Calls
89
+ # {Client#get_object_acl} if {#data_loaded?} is `false`.
90
+ def data
91
+ load unless @data
92
+ @data
93
+ end
94
+
95
+ # @return [Boolean]
96
+ # Returns `true` if this resource is loaded. Accessing attributes or
97
+ # {#data} on an unloaded resource will trigger a call to {#load}.
98
+ def data_loaded?
99
+ !!@data
100
+ end
101
+
102
+ # @deprecated Use [Aws::S3::Client] #wait_until instead
103
+ #
104
+ # Waiter polls an API operation until a resource enters a desired
105
+ # state.
106
+ #
107
+ # @note The waiting operation is performed on a copy. The original resource
108
+ # remains unchanged.
109
+ #
110
+ # ## Basic Usage
111
+ #
112
+ # Waiter will polls until it is successful, it fails by
113
+ # entering a terminal state, or until a maximum number of attempts
114
+ # are made.
115
+ #
116
+ # # polls in a loop until condition is true
117
+ # resource.wait_until(options) {|resource| condition}
118
+ #
119
+ # ## Example
120
+ #
121
+ # instance.wait_until(max_attempts:10, delay:5) do |instance|
122
+ # instance.state.name == 'running'
123
+ # end
124
+ #
125
+ # ## Configuration
126
+ #
127
+ # You can configure the maximum number of polling attempts, and the
128
+ # delay (in seconds) between each polling attempt. The waiting condition is
129
+ # set by passing a block to {#wait_until}:
130
+ #
131
+ # # poll for ~25 seconds
132
+ # resource.wait_until(max_attempts:5,delay:5) {|resource|...}
133
+ #
134
+ # ## Callbacks
135
+ #
136
+ # You can be notified before each polling attempt and before each
137
+ # delay. If you throw `:success` or `:failure` from these callbacks,
138
+ # it will terminate the waiter.
139
+ #
140
+ # started_at = Time.now
141
+ # # poll for 1 hour, instead of a number of attempts
142
+ # proc = Proc.new do |attempts, response|
143
+ # throw :failure if Time.now - started_at > 3600
144
+ # end
145
+ #
146
+ # # disable max attempts
147
+ # instance.wait_until(before_wait:proc, max_attempts:nil) {...}
148
+ #
149
+ # ## Handling Errors
150
+ #
151
+ # When a waiter is successful, it returns the Resource. When a waiter
152
+ # fails, it raises an error.
153
+ #
154
+ # begin
155
+ # resource.wait_until(...)
156
+ # rescue Aws::Waiters::Errors::WaiterFailed
157
+ # # resource did not enter the desired state in time
158
+ # end
159
+ #
160
+ # @yieldparam [Resource] resource to be used in the waiting condition.
161
+ #
162
+ # @raise [Aws::Waiters::Errors::FailureStateError] Raised when the waiter
163
+ # terminates because the waiter has entered a state that it will not
164
+ # transition out of, preventing success.
165
+ #
166
+ # yet successful.
167
+ #
168
+ # @raise [Aws::Waiters::Errors::UnexpectedError] Raised when an error is
169
+ # encountered while polling for a resource that is not expected.
170
+ #
171
+ # @raise [NotImplementedError] Raised when the resource does not
172
+ #
173
+ # @option options [Integer] :max_attempts (10) Maximum number of
174
+ # attempts
175
+ # @option options [Integer] :delay (10) Delay between each
176
+ # attempt in seconds
177
+ # @option options [Proc] :before_attempt (nil) Callback
178
+ # invoked before each attempt
179
+ # @option options [Proc] :before_wait (nil) Callback
180
+ # invoked before each wait
181
+ # @return [Resource] if the waiter was successful
182
+ def wait_until(options = {}, &block)
183
+ self_copy = self.dup
184
+ attempts = 0
185
+ options[:max_attempts] = 10 unless options.key?(:max_attempts)
186
+ options[:delay] ||= 10
187
+ options[:poller] = Proc.new do
188
+ attempts += 1
189
+ if block.call(self_copy)
190
+ [:success, self_copy]
191
+ else
192
+ self_copy.reload unless attempts == options[:max_attempts]
193
+ :retry
194
+ end
195
+ end
196
+ Aws::Waiters::Waiter.new(options).wait({})
197
+ end
198
+
199
+ # @!group Actions
200
+
201
+ # @example Request syntax with placeholder values
202
+ #
203
+ # object_acl.put({
204
+ # acl: "private", # accepts private, public-read, public-read-write, authenticated-read, aws-exec-read, bucket-owner-read, bucket-owner-full-control
205
+ # access_control_policy: {
206
+ # grants: [
207
+ # {
208
+ # grantee: {
209
+ # display_name: "DisplayName",
210
+ # email_address: "EmailAddress",
211
+ # id: "ID",
212
+ # type: "CanonicalUser", # required, accepts CanonicalUser, AmazonCustomerByEmail, Group
213
+ # uri: "URI",
214
+ # },
215
+ # permission: "FULL_CONTROL", # accepts FULL_CONTROL, WRITE, WRITE_ACP, READ, READ_ACP
216
+ # },
217
+ # ],
218
+ # owner: {
219
+ # display_name: "DisplayName",
220
+ # id: "ID",
221
+ # },
222
+ # },
223
+ # content_md5: "ContentMD5",
224
+ # grant_full_control: "GrantFullControl",
225
+ # grant_read: "GrantRead",
226
+ # grant_read_acp: "GrantReadACP",
227
+ # grant_write: "GrantWrite",
228
+ # grant_write_acp: "GrantWriteACP",
229
+ # request_payer: "requester", # accepts requester
230
+ # version_id: "ObjectVersionId",
231
+ # })
232
+ # @param [Hash] options ({})
233
+ # @option options [String] :acl
234
+ # The canned ACL to apply to the object. For more information, see
235
+ # [Canned ACL][1].
236
+ #
237
+ #
238
+ #
239
+ # [1]: https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#CannedACL
240
+ # @option options [Types::AccessControlPolicy] :access_control_policy
241
+ # Contains the elements that set the ACL permissions for an object per
242
+ # grantee.
243
+ # @option options [String] :content_md5
244
+ # The base64-encoded 128-bit MD5 digest of the data. This header must be
245
+ # used as a message integrity check to verify that the request body was
246
+ # not corrupted in transit. For more information, go to [RFC
247
+ # 1864.&gt;][1]
248
+ #
249
+ #
250
+ #
251
+ # [1]: http://www.ietf.org/rfc/rfc1864.txt
252
+ # @option options [String] :grant_full_control
253
+ # Allows grantee the read, write, read ACP, and write ACP permissions on
254
+ # the bucket.
255
+ # @option options [String] :grant_read
256
+ # Allows grantee to list the objects in the bucket.
257
+ # @option options [String] :grant_read_acp
258
+ # Allows grantee to read the bucket ACL.
259
+ # @option options [String] :grant_write
260
+ # Allows grantee to create, overwrite, and delete any object in the
261
+ # bucket.
262
+ # @option options [String] :grant_write_acp
263
+ # Allows grantee to write the ACL for the applicable bucket.
264
+ # @option options [String] :request_payer
265
+ # Confirms that the requester knows that they will be charged for the
266
+ # request. Bucket owners need not specify this parameter in their
267
+ # requests. For information about downloading objects from requester
268
+ # pays buckets, see [Downloading Objects in Requestor Pays Buckets][1]
269
+ # in the *Amazon S3 Developer Guide*.
270
+ #
271
+ #
272
+ #
273
+ # [1]: https://docs.aws.amazon.com/AmazonS3/latest/dev/ObjectsinRequesterPaysBuckets.html
274
+ # @option options [String] :version_id
275
+ # VersionId used to reference a specific version of the object.
276
+ # @return [Types::PutObjectAclOutput]
277
+ def put(options = {})
278
+ options = options.merge(
279
+ bucket: @bucket_name,
280
+ key: @object_key
281
+ )
282
+ resp = @client.put_object_acl(options)
283
+ resp.data
284
+ end
285
+
286
+ # @!group Associations
287
+
288
+ # @return [Object]
289
+ def object
290
+ Object.new(
291
+ bucket_name: @bucket_name,
292
+ key: @object_key,
293
+ client: @client
294
+ )
295
+ end
296
+
297
+ # @deprecated
298
+ # @api private
299
+ def identifiers
300
+ {
301
+ bucket_name: @bucket_name,
302
+ object_key: @object_key
303
+ }
304
+ end
305
+ deprecated(:identifiers)
306
+
307
+ private
308
+
309
+ def extract_bucket_name(args, options)
310
+ value = args[0] || options.delete(:bucket_name)
311
+ case value
312
+ when String then value
313
+ when nil then raise ArgumentError, "missing required option :bucket_name"
314
+ else
315
+ msg = "expected :bucket_name to be a String, got #{value.class}"
316
+ raise ArgumentError, msg
317
+ end
318
+ end
319
+
320
+ def extract_object_key(args, options)
321
+ value = args[1] || options.delete(:object_key)
322
+ case value
323
+ when String then value
324
+ when nil then raise ArgumentError, "missing required option :object_key"
325
+ else
326
+ msg = "expected :object_key to be a String, got #{value.class}"
327
+ raise ArgumentError, msg
328
+ end
329
+ end
330
+
331
+ class Collection < Aws::Resources::Collection; end
332
+ end
333
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thread'
4
+
5
+ module Aws
6
+ module S3
7
+ # @api private
8
+ class ObjectCopier
9
+
10
+ # @param [S3::Object] object
11
+ def initialize(object, options = {})
12
+ @object = object
13
+ @options = options.merge(client: @object.client)
14
+ end
15
+
16
+ def copy_from(source, options = {})
17
+ copy_object(source, @object, merge_options(source, options))
18
+ end
19
+
20
+ def copy_to(target, options = {})
21
+ copy_object(@object, target, merge_options(target, options))
22
+ end
23
+
24
+ private
25
+
26
+ def copy_object(source, target, options)
27
+ target_bucket, target_key = copy_target(target)
28
+ options[:bucket] = target_bucket
29
+ options[:key] = target_key
30
+ options[:copy_source] = copy_source(source)
31
+ if options.delete(:multipart_copy)
32
+ apply_source_client(source, options)
33
+ ObjectMultipartCopier.new(@options).copy(options)
34
+ else
35
+ @object.client.copy_object(options)
36
+ end
37
+ end
38
+
39
+ def copy_source(source)
40
+ case source
41
+ when String then source
42
+ when Hash
43
+ src = "#{source[:bucket]}/#{escape(source[:key])}"
44
+ src += "?versionId=#{source[:version_id]}" if source.key?(:version_id)
45
+ src
46
+ when S3::Object, S3::ObjectSummary
47
+ "#{source.bucket_name}/#{escape(source.key)}"
48
+ when S3::ObjectVersion
49
+ "#{source.bucket_name}/#{escape(source.object_key)}?versionId=#{source.id}"
50
+ else
51
+ msg = "expected source to be an Aws::S3::Object, Hash, or String"
52
+ raise ArgumentError, msg
53
+ end
54
+ end
55
+
56
+ def copy_target(target)
57
+ case target
58
+ when String then target.match(/([^\/]+?)\/(.+)/)[1,2]
59
+ when Hash then target.values_at(:bucket, :key)
60
+ when S3::Object then [target.bucket_name, target.key]
61
+ else
62
+ msg = "expected target to be an Aws::S3::Object, Hash, or String"
63
+ raise ArgumentError, msg
64
+ end
65
+ end
66
+
67
+ def merge_options(source_or_target, options)
68
+ if Hash === source_or_target
69
+ source_or_target.inject(options.dup) do |opts, (key, value)|
70
+ opts[key] = value unless [:bucket, :key, :version_id].include?(key)
71
+ opts
72
+ end
73
+ else
74
+ options.dup
75
+ end
76
+ end
77
+
78
+ def apply_source_client(source, options)
79
+
80
+ if source.respond_to?(:client)
81
+ options[:copy_source_client] ||= source.client
82
+ end
83
+
84
+ if options[:copy_source_region]
85
+ config = @object.client.config
86
+ config = config.each_pair.inject({}) { |h, (k,v)| h[k] = v; h }
87
+ config[:region] = options.delete(:copy_source_region)
88
+ options[:copy_source_client] ||= S3::Client.new(config)
89
+ end
90
+
91
+ options[:copy_source_client] ||= @object.client
92
+
93
+ end
94
+
95
+ def escape(str)
96
+ Seahorse::Util.uri_path_escape(str)
97
+ end
98
+
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,182 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thread'
4
+ require 'cgi'
5
+
6
+ module Aws
7
+ module S3
8
+ # @api private
9
+ class ObjectMultipartCopier
10
+
11
+ FIVE_MB = 5 * 1024 * 1024 # 5MB
12
+
13
+ FILE_TOO_SMALL = "unable to multipart copy files smaller than 5MB"
14
+
15
+ MAX_PARTS = 10_000
16
+
17
+ # @option options [Client] :client
18
+ # @option [Integer] :min_part_size (52428800) Size of copied parts.
19
+ # Defaults to 50MB.
20
+ # will be constructed from the given `options' hash.
21
+ # @option [Integer] :thread_count (10) Number of concurrent threads to
22
+ # use for copying parts.
23
+ def initialize(options = {})
24
+ @thread_count = options.delete(:thread_count) || 10
25
+ @min_part_size = options.delete(:min_part_size) || (FIVE_MB * 10)
26
+ @client = options[:client] || Client.new
27
+ end
28
+
29
+ # @return [Client]
30
+ attr_reader :client
31
+
32
+ # @option (see S3::Client#copy_object)
33
+ def copy(options = {})
34
+ size = source_size(options)
35
+ options[:upload_id] = initiate_upload(options)
36
+ begin
37
+ parts = copy_parts(size, default_part_size(size), options)
38
+ complete_upload(parts, options)
39
+ rescue => error
40
+ abort_upload(options)
41
+ raise error
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def initiate_upload(options)
48
+ options = options_for(:create_multipart_upload, options)
49
+ @client.create_multipart_upload(options).upload_id
50
+ end
51
+
52
+ def copy_parts(size, default_part_size, options)
53
+ queue = PartQueue.new(compute_parts(size, default_part_size, options))
54
+ threads = []
55
+ @thread_count.times do
56
+ threads << copy_part_thread(queue)
57
+ end
58
+ threads.map(&:value).flatten.sort_by{ |part| part[:part_number] }
59
+ end
60
+
61
+ def copy_part_thread(queue)
62
+ Thread.new do
63
+ begin
64
+ completed = []
65
+ while part = queue.shift
66
+ completed << copy_part(part)
67
+ end
68
+ completed
69
+ rescue => error
70
+ queue.clear!
71
+ raise error
72
+ end
73
+ end
74
+ end
75
+
76
+ def copy_part(part)
77
+ {
78
+ etag: @client.upload_part_copy(part).copy_part_result.etag,
79
+ part_number: part[:part_number],
80
+ }
81
+ end
82
+
83
+ def complete_upload(parts, options)
84
+ options = options_for(:complete_multipart_upload, options)
85
+ options[:multipart_upload] = { parts: parts }
86
+ @client.complete_multipart_upload(options)
87
+ end
88
+
89
+ def abort_upload(options)
90
+ @client.abort_multipart_upload({
91
+ bucket: options[:bucket],
92
+ key: options[:key],
93
+ upload_id: options[:upload_id],
94
+ })
95
+ end
96
+
97
+ def compute_parts(size, default_part_size, options)
98
+ part_number = 1
99
+ offset = 0
100
+ parts = []
101
+ options = options_for(:upload_part_copy, options)
102
+ while offset < size
103
+ parts << options.merge({
104
+ part_number: part_number,
105
+ copy_source_range: byte_range(offset, default_part_size, size),
106
+ })
107
+ part_number += 1
108
+ offset += default_part_size
109
+ end
110
+ parts
111
+ end
112
+
113
+ def byte_range(offset, default_part_size, size)
114
+ if offset + default_part_size < size
115
+ "bytes=#{offset}-#{offset + default_part_size - 1}"
116
+ else
117
+ "bytes=#{offset}-#{size - 1}"
118
+ end
119
+ end
120
+
121
+ def source_size(options)
122
+ return options.delete(:content_length) if options[:content_length]
123
+
124
+ client = options[:copy_source_client] || @client
125
+
126
+ if vid_match = options[:copy_source].match(/([^\/]+?)\/(.+)\?versionId=(.+)/)
127
+ bucket, key, version_id = vid_match[1,3]
128
+ else
129
+ bucket, key = options[:copy_source].match(/([^\/]+?)\/(.+)/)[1,2]
130
+ end
131
+
132
+ key = CGI.unescape(key)
133
+ opts = { bucket: bucket, key: key }
134
+ opts[:version_id] = version_id if version_id
135
+ client.head_object(opts).content_length
136
+ end
137
+
138
+ def default_part_size(source_size)
139
+ if source_size < FIVE_MB
140
+ raise ArgumentError, FILE_TOO_SMALL
141
+ else
142
+ [(source_size.to_f / MAX_PARTS).ceil, @min_part_size].max.to_i
143
+ end
144
+ end
145
+
146
+ def options_for(operation_name, options)
147
+ API_OPTIONS[operation_name].inject({}) do |hash, opt_name|
148
+ hash[opt_name] = options[opt_name] if options.key?(opt_name)
149
+ hash
150
+ end
151
+ end
152
+
153
+ # @api private
154
+ def self.options_for(shape_name)
155
+ Client.api.metadata['shapes'][shape_name].member_names
156
+ end
157
+
158
+ API_OPTIONS = {
159
+ create_multipart_upload: Types::CreateMultipartUploadRequest.members,
160
+ upload_part_copy: Types::UploadPartCopyRequest.members,
161
+ complete_multipart_upload: Types::CompleteMultipartUploadRequest.members,
162
+ }
163
+
164
+ class PartQueue
165
+
166
+ def initialize(parts = [])
167
+ @parts = parts
168
+ @mutex = Mutex.new
169
+ end
170
+
171
+ def shift
172
+ @mutex.synchronize { @parts.shift }
173
+ end
174
+
175
+ def clear!
176
+ @mutex.synchronize { @parts.clear }
177
+ end
178
+
179
+ end
180
+ end
181
+ end
182
+ end