aws-sdk-s3 1.75.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.
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