aws-sdk-s3 1.128.0 → 1.199.1
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 +450 -1
- data/VERSION +1 -1
- 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 +720 -128
- data/lib/aws-sdk-s3/bucket_acl.rb +18 -17
- data/lib/aws-sdk-s3/bucket_cors.rb +22 -21
- data/lib/aws-sdk-s3/bucket_lifecycle.rb +23 -18
- data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +76 -19
- data/lib/aws-sdk-s3/bucket_logging.rb +21 -14
- data/lib/aws-sdk-s3/bucket_notification.rb +6 -6
- data/lib/aws-sdk-s3/bucket_policy.rb +65 -20
- data/lib/aws-sdk-s3/bucket_region_cache.rb +9 -5
- data/lib/aws-sdk-s3/bucket_request_payment.rb +15 -15
- data/lib/aws-sdk-s3/bucket_tagging.rb +19 -19
- data/lib/aws-sdk-s3/bucket_versioning.rb +41 -41
- data/lib/aws-sdk-s3/bucket_website.rb +19 -19
- data/lib/aws-sdk-s3/client.rb +9352 -3264
- data/lib/aws-sdk-s3/client_api.rb +697 -164
- data/lib/aws-sdk-s3/customizations/bucket.rb +1 -1
- data/lib/aws-sdk-s3/customizations/errors.rb +16 -3
- data/lib/aws-sdk-s3/customizations/object.rb +112 -56
- data/lib/aws-sdk-s3/customizations/object_summary.rb +5 -0
- data/lib/aws-sdk-s3/customizations/object_version.rb +13 -0
- data/lib/aws-sdk-s3/customizations.rb +26 -31
- data/lib/aws-sdk-s3/encryption/client.rb +2 -2
- data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +2 -2
- data/lib/aws-sdk-s3/encryptionV2/client.rb +2 -2
- data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +2 -2
- data/lib/aws-sdk-s3/endpoint_parameters.rb +54 -15
- data/lib/aws-sdk-s3/endpoint_provider.rb +439 -456
- data/lib/aws-sdk-s3/endpoints.rb +629 -1261
- data/lib/aws-sdk-s3/errors.rb +58 -0
- 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 +156 -69
- data/lib/aws-sdk-s3/file_uploader.rb +4 -6
- 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 +56 -69
- data/lib/aws-sdk-s3/multipart_stream_uploader.rb +84 -91
- data/lib/aws-sdk-s3/multipart_upload.rb +179 -26
- data/lib/aws-sdk-s3/multipart_upload_error.rb +3 -4
- data/lib/aws-sdk-s3/multipart_upload_part.rb +201 -60
- data/lib/aws-sdk-s3/object.rb +2007 -281
- data/lib/aws-sdk-s3/object_acl.rb +43 -23
- data/lib/aws-sdk-s3/object_copier.rb +1 -1
- data/lib/aws-sdk-s3/object_multipart_copier.rb +44 -25
- data/lib/aws-sdk-s3/object_summary.rb +1735 -232
- data/lib/aws-sdk-s3/object_version.rb +394 -52
- data/lib/aws-sdk-s3/plugins/access_grants.rb +178 -0
- data/lib/aws-sdk-s3/plugins/checksum_algorithm.rb +31 -0
- data/lib/aws-sdk-s3/plugins/endpoints.rb +32 -208
- data/lib/aws-sdk-s3/plugins/express_session_auth.rb +88 -0
- data/lib/aws-sdk-s3/plugins/http_200_errors.rb +55 -18
- data/lib/aws-sdk-s3/plugins/location_constraint.rb +3 -1
- data/lib/aws-sdk-s3/plugins/md5s.rb +10 -70
- data/lib/aws-sdk-s3/plugins/s3_signer.rb +7 -2
- 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/presigned_post.rb +52 -43
- data/lib/aws-sdk-s3/presigner.rb +9 -7
- data/lib/aws-sdk-s3/resource.rb +127 -22
- data/lib/aws-sdk-s3/transfer_manager.rb +252 -0
- data/lib/aws-sdk-s3/types.rb +8068 -1887
- data/lib/aws-sdk-s3.rb +35 -31
- data/sig/bucket.rbs +231 -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 +2584 -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 +44 -0
- data/sig/multipart_upload.rbs +120 -0
- data/sig/multipart_upload_part.rbs +109 -0
- data/sig/object.rbs +462 -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 +141 -0
- data/sig/types.rbs +2866 -0
- data/sig/waiters.rbs +95 -0
- metadata +44 -12
- data/lib/aws-sdk-s3/plugins/skip_whole_multipart_get_checksums.rb +0 -31
@@ -9,32 +9,22 @@ module Aws
|
|
9
9
|
class MultipartFileUploader
|
10
10
|
|
11
11
|
MIN_PART_SIZE = 5 * 1024 * 1024 # 5MB
|
12
|
-
|
13
|
-
FILE_TOO_SMALL = "unable to multipart upload files smaller than 5MB"
|
14
|
-
|
15
12
|
MAX_PARTS = 10_000
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
Client.api.operation(:
|
22
|
-
|
23
|
-
|
24
|
-
COMPLETE_OPTIONS = Set.new(
|
25
|
-
Client.api.operation(:complete_multipart_upload).input.shape.member_names
|
26
|
-
)
|
27
|
-
|
28
|
-
# @api private
|
29
|
-
UPLOAD_PART_OPTIONS = Set.new(
|
30
|
-
Client.api.operation(:upload_part).input.shape.member_names
|
13
|
+
DEFAULT_THREAD_COUNT = 10
|
14
|
+
CREATE_OPTIONS = Set.new(Client.api.operation(:create_multipart_upload).input.shape.member_names)
|
15
|
+
COMPLETE_OPTIONS = Set.new(Client.api.operation(:complete_multipart_upload).input.shape.member_names)
|
16
|
+
UPLOAD_PART_OPTIONS = Set.new(Client.api.operation(:upload_part).input.shape.member_names)
|
17
|
+
CHECKSUM_KEYS = Set.new(
|
18
|
+
Client.api.operation(:upload_part).input.shape.members.map do |n, s|
|
19
|
+
n if s.location == 'header' && s.location_name.start_with?('x-amz-checksum-')
|
20
|
+
end.compact
|
31
21
|
)
|
32
22
|
|
33
23
|
# @option options [Client] :client
|
34
|
-
# @option options [Integer] :thread_count (
|
24
|
+
# @option options [Integer] :thread_count (DEFAULT_THREAD_COUNT)
|
35
25
|
def initialize(options = {})
|
36
26
|
@client = options[:client] || Client.new
|
37
|
-
@thread_count = options[:thread_count] ||
|
27
|
+
@thread_count = options[:thread_count] || DEFAULT_THREAD_COUNT
|
38
28
|
end
|
39
29
|
|
40
30
|
# @return [Client]
|
@@ -48,13 +38,11 @@ module Aws
|
|
48
38
|
# It will be invoked with [bytes_read], [total_sizes]
|
49
39
|
# @return [Seahorse::Client::Response] - the CompleteMultipartUploadResponse
|
50
40
|
def upload(source, options = {})
|
51
|
-
if File.size(source) < MIN_PART_SIZE
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
complete_upload(upload_id, parts, options)
|
57
|
-
end
|
41
|
+
raise ArgumentError, 'unable to multipart upload files smaller than 5MB' if File.size(source) < MIN_PART_SIZE
|
42
|
+
|
43
|
+
upload_id = initiate_upload(options)
|
44
|
+
parts = upload_parts(upload_id, source, options)
|
45
|
+
complete_upload(upload_id, parts, source, options)
|
58
46
|
end
|
59
47
|
|
60
48
|
private
|
@@ -63,18 +51,21 @@ module Aws
|
|
63
51
|
@client.create_multipart_upload(create_opts(options)).upload_id
|
64
52
|
end
|
65
53
|
|
66
|
-
def complete_upload(upload_id, parts, options)
|
54
|
+
def complete_upload(upload_id, parts, source, options)
|
67
55
|
@client.complete_multipart_upload(
|
68
56
|
**complete_opts(options).merge(
|
69
57
|
upload_id: upload_id,
|
70
|
-
multipart_upload: { parts: parts }
|
58
|
+
multipart_upload: { parts: parts },
|
59
|
+
mpu_object_size: File.size(source)
|
71
60
|
)
|
72
61
|
)
|
62
|
+
rescue StandardError => e
|
63
|
+
abort_upload(upload_id, options, [e])
|
73
64
|
end
|
74
65
|
|
75
66
|
def upload_parts(upload_id, source, options)
|
76
|
-
pending = PartList.new(compute_parts(upload_id, source, options))
|
77
67
|
completed = PartList.new
|
68
|
+
pending = PartList.new(compute_parts(upload_id, source, options))
|
78
69
|
errors = upload_in_threads(pending, completed, options)
|
79
70
|
if errors.empty?
|
80
71
|
completed.to_a.sort_by { |part| part[:part_number] }
|
@@ -84,18 +75,15 @@ module Aws
|
|
84
75
|
end
|
85
76
|
|
86
77
|
def abort_upload(upload_id, options, errors)
|
87
|
-
@client.abort_multipart_upload(
|
88
|
-
|
89
|
-
key: options[:key],
|
90
|
-
upload_id: upload_id
|
91
|
-
)
|
92
|
-
msg = "multipart upload failed: #{errors.map(&:message).join("; ")}"
|
78
|
+
@client.abort_multipart_upload(bucket: options[:bucket], key: options[:key], upload_id: upload_id)
|
79
|
+
msg = "multipart upload failed: #{errors.map(&:message).join('; ')}"
|
93
80
|
raise MultipartUploadError.new(msg, errors)
|
94
|
-
rescue MultipartUploadError =>
|
95
|
-
raise
|
96
|
-
rescue =>
|
97
|
-
msg = "failed to abort multipart upload: #{
|
98
|
-
|
81
|
+
rescue MultipartUploadError => e
|
82
|
+
raise e
|
83
|
+
rescue StandardError => e
|
84
|
+
msg = "failed to abort multipart upload: #{e.message}. "\
|
85
|
+
"Multipart upload failed: #{errors.map(&:message).join('; ')}"
|
86
|
+
raise MultipartUploadError.new(msg, errors + [e])
|
99
87
|
end
|
100
88
|
|
101
89
|
def compute_parts(upload_id, source, options)
|
@@ -108,11 +96,7 @@ module Aws
|
|
108
96
|
parts << upload_part_opts(options).merge(
|
109
97
|
upload_id: upload_id,
|
110
98
|
part_number: part_number,
|
111
|
-
body: FilePart.new(
|
112
|
-
source: source,
|
113
|
-
offset: offset,
|
114
|
-
size: part_size(size, default_part_size, offset)
|
115
|
-
)
|
99
|
+
body: FilePart.new(source: source, offset: offset, size: part_size(size, default_part_size, offset))
|
116
100
|
)
|
117
101
|
part_number += 1
|
118
102
|
offset += default_part_size
|
@@ -120,24 +104,34 @@ module Aws
|
|
120
104
|
parts
|
121
105
|
end
|
122
106
|
|
107
|
+
def checksum_key?(key)
|
108
|
+
CHECKSUM_KEYS.include?(key)
|
109
|
+
end
|
110
|
+
|
111
|
+
def has_checksum_key?(keys)
|
112
|
+
keys.any? { |key| checksum_key?(key) }
|
113
|
+
end
|
114
|
+
|
123
115
|
def create_opts(options)
|
124
|
-
|
116
|
+
opts = { checksum_algorithm: Aws::Plugins::ChecksumAlgorithm::DEFAULT_CHECKSUM }
|
117
|
+
opts[:checksum_type] = 'FULL_OBJECT' if has_checksum_key?(options.keys)
|
118
|
+
CREATE_OPTIONS.each_with_object(opts) do |key, hash|
|
125
119
|
hash[key] = options[key] if options.key?(key)
|
126
|
-
hash
|
127
120
|
end
|
128
121
|
end
|
129
122
|
|
130
123
|
def complete_opts(options)
|
131
|
-
|
124
|
+
opts = {}
|
125
|
+
opts[:checksum_type] = 'FULL_OBJECT' if has_checksum_key?(options.keys)
|
126
|
+
COMPLETE_OPTIONS.each_with_object(opts) do |key, hash|
|
132
127
|
hash[key] = options[key] if options.key?(key)
|
133
|
-
hash
|
134
128
|
end
|
135
129
|
end
|
136
130
|
|
137
131
|
def upload_part_opts(options)
|
138
|
-
UPLOAD_PART_OPTIONS.
|
139
|
-
|
140
|
-
hash
|
132
|
+
UPLOAD_PART_OPTIONS.each_with_object({}) do |key, hash|
|
133
|
+
# don't pass through checksum calculations
|
134
|
+
hash[key] = options[key] if options.key?(key) && !checksum_key?(key)
|
141
135
|
end
|
142
136
|
end
|
143
137
|
|
@@ -146,10 +140,10 @@ module Aws
|
|
146
140
|
if (callback = options[:progress_callback])
|
147
141
|
progress = MultipartProgress.new(pending, callback)
|
148
142
|
end
|
149
|
-
@thread_count.times do
|
143
|
+
options.fetch(:thread_count, @thread_count).times do
|
150
144
|
thread = Thread.new do
|
151
145
|
begin
|
152
|
-
while part = pending.shift
|
146
|
+
while (part = pending.shift)
|
153
147
|
if progress
|
154
148
|
part[:on_chunk_sent] =
|
155
149
|
proc do |_chunk, bytes, _total|
|
@@ -158,24 +152,19 @@ module Aws
|
|
158
152
|
end
|
159
153
|
resp = @client.upload_part(part)
|
160
154
|
part[:body].close
|
161
|
-
completed_part = {etag: resp.etag, part_number: part[:part_number]}
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
k = "checksum_#{part[:checksum_algorithm].downcase}".to_sym
|
166
|
-
completed_part[k] = resp[k]
|
167
|
-
end
|
168
|
-
|
155
|
+
completed_part = { etag: resp.etag, part_number: part[:part_number] }
|
156
|
+
algorithm = resp.context.params[:checksum_algorithm]
|
157
|
+
k = "checksum_#{algorithm.downcase}".to_sym
|
158
|
+
completed_part[k] = resp.send(k)
|
169
159
|
completed.push(completed_part)
|
170
160
|
end
|
171
161
|
nil
|
172
|
-
rescue =>
|
162
|
+
rescue StandardError => e
|
173
163
|
# keep other threads from uploading other parts
|
174
164
|
pending.clear!
|
175
|
-
|
165
|
+
e
|
176
166
|
end
|
177
167
|
end
|
178
|
-
thread.abort_on_exception = true
|
179
168
|
threads << thread
|
180
169
|
end
|
181
170
|
threads.map(&:value).compact
|
@@ -195,7 +184,6 @@ module Aws
|
|
195
184
|
|
196
185
|
# @api private
|
197
186
|
class PartList
|
198
|
-
|
199
187
|
def initialize(parts = [])
|
200
188
|
@parts = parts
|
201
189
|
@mutex = Mutex.new
|
@@ -224,7 +212,6 @@ module Aws
|
|
224
212
|
def to_a
|
225
213
|
@mutex.synchronize { @parts.dup }
|
226
214
|
end
|
227
|
-
|
228
215
|
end
|
229
216
|
|
230
217
|
# @api private
|
@@ -243,4 +230,4 @@ module Aws
|
|
243
230
|
end
|
244
231
|
end
|
245
232
|
end
|
246
|
-
end
|
233
|
+
end
|
@@ -9,33 +9,19 @@ module Aws
|
|
9
9
|
module S3
|
10
10
|
# @api private
|
11
11
|
class MultipartStreamUploader
|
12
|
-
# api private
|
13
|
-
PART_SIZE = 5 * 1024 * 1024 # 5MB
|
14
12
|
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
# @api private
|
22
|
-
CREATE_OPTIONS =
|
23
|
-
Set.new(Client.api.operation(:create_multipart_upload).input.shape.member_names)
|
24
|
-
|
25
|
-
# @api private
|
26
|
-
UPLOAD_PART_OPTIONS =
|
27
|
-
Set.new(Client.api.operation(:upload_part).input.shape.member_names)
|
28
|
-
|
29
|
-
# @api private
|
30
|
-
COMPLETE_UPLOAD_OPTIONS =
|
31
|
-
Set.new(Client.api.operation(:complete_multipart_upload).input.shape.member_names)
|
13
|
+
DEFAULT_PART_SIZE = 5 * 1024 * 1024 # 5MB
|
14
|
+
DEFAULT_THREAD_COUNT = 10
|
15
|
+
CREATE_OPTIONS = Set.new(Client.api.operation(:create_multipart_upload).input.shape.member_names)
|
16
|
+
UPLOAD_PART_OPTIONS = Set.new(Client.api.operation(:upload_part).input.shape.member_names)
|
17
|
+
COMPLETE_UPLOAD_OPTIONS = Set.new(Client.api.operation(:complete_multipart_upload).input.shape.member_names)
|
32
18
|
|
33
19
|
# @option options [Client] :client
|
34
20
|
def initialize(options = {})
|
35
21
|
@client = options[:client] || Client.new
|
36
22
|
@tempfile = options[:tempfile]
|
37
|
-
@part_size = options[:part_size] ||
|
38
|
-
@thread_count = options[:thread_count] ||
|
23
|
+
@part_size = options[:part_size] || DEFAULT_PART_SIZE
|
24
|
+
@thread_count = options[:thread_count] || DEFAULT_THREAD_COUNT
|
39
25
|
end
|
40
26
|
|
41
27
|
# @return [Client]
|
@@ -43,9 +29,10 @@ module Aws
|
|
43
29
|
|
44
30
|
# @option options [required,String] :bucket
|
45
31
|
# @option options [required,String] :key
|
32
|
+
# @option options [Integer] :thread_count (DEFAULT_THREAD_COUNT)
|
46
33
|
# @return [Seahorse::Client::Response] - the CompleteMultipartUploadResponse
|
47
34
|
def upload(options = {}, &block)
|
48
|
-
Aws::Plugins::UserAgent.
|
35
|
+
Aws::Plugins::UserAgent.metric('S3_TRANSFER') do
|
49
36
|
upload_id = initiate_upload(options)
|
50
37
|
parts = upload_parts(upload_id, options, &block)
|
51
38
|
complete_upload(upload_id, parts, options)
|
@@ -60,11 +47,10 @@ module Aws
|
|
60
47
|
|
61
48
|
def complete_upload(upload_id, parts, options)
|
62
49
|
@client.complete_multipart_upload(
|
63
|
-
**complete_opts(options).merge(
|
64
|
-
upload_id: upload_id,
|
65
|
-
multipart_upload: { parts: parts }
|
66
|
-
)
|
50
|
+
**complete_opts(options).merge(upload_id: upload_id, multipart_upload: { parts: parts })
|
67
51
|
)
|
52
|
+
rescue StandardError => e
|
53
|
+
abort_upload(upload_id, options, [e])
|
68
54
|
end
|
69
55
|
|
70
56
|
def upload_parts(upload_id, options, &block)
|
@@ -73,9 +59,11 @@ module Aws
|
|
73
59
|
errors = begin
|
74
60
|
IO.pipe do |read_pipe, write_pipe|
|
75
61
|
threads = upload_in_threads(
|
76
|
-
read_pipe,
|
62
|
+
read_pipe,
|
63
|
+
completed,
|
77
64
|
upload_part_opts(options).merge(upload_id: upload_id),
|
78
|
-
thread_errors
|
65
|
+
thread_errors
|
66
|
+
)
|
79
67
|
begin
|
80
68
|
block.call(write_pipe)
|
81
69
|
ensure
|
@@ -84,61 +72,53 @@ module Aws
|
|
84
72
|
end
|
85
73
|
threads.map(&:value).compact
|
86
74
|
end
|
87
|
-
rescue => e
|
75
|
+
rescue StandardError => e
|
88
76
|
thread_errors + [e]
|
89
77
|
end
|
78
|
+
return ordered_parts(completed) if errors.empty?
|
90
79
|
|
91
|
-
|
92
|
-
Array.new(completed.size) { completed.pop }.sort_by { |part| part[:part_number] }
|
93
|
-
else
|
94
|
-
abort_upload(upload_id, options, errors)
|
95
|
-
end
|
80
|
+
abort_upload(upload_id, options, errors)
|
96
81
|
end
|
97
82
|
|
98
83
|
def abort_upload(upload_id, options, errors)
|
99
|
-
@client.abort_multipart_upload(
|
100
|
-
|
101
|
-
key: options[:key],
|
102
|
-
upload_id: upload_id
|
103
|
-
)
|
104
|
-
msg = "multipart upload failed: #{errors.map(&:message).join("; ")}"
|
84
|
+
@client.abort_multipart_upload(bucket: options[:bucket], key: options[:key], upload_id: upload_id)
|
85
|
+
msg = "multipart upload failed: #{errors.map(&:message).join('; ')}"
|
105
86
|
raise MultipartUploadError.new(msg, errors)
|
106
|
-
rescue MultipartUploadError =>
|
107
|
-
raise
|
108
|
-
rescue =>
|
109
|
-
msg = "failed to abort multipart upload: #{
|
110
|
-
|
87
|
+
rescue MultipartUploadError => e
|
88
|
+
raise e
|
89
|
+
rescue StandardError => e
|
90
|
+
msg = "failed to abort multipart upload: #{e.message}. "\
|
91
|
+
"Multipart upload failed: #{errors.map(&:message).join('; ')}"
|
92
|
+
raise MultipartUploadError.new(msg, errors + [e])
|
111
93
|
end
|
112
94
|
|
113
95
|
def create_opts(options)
|
114
|
-
CREATE_OPTIONS.
|
96
|
+
CREATE_OPTIONS.each_with_object({}) do |key, hash|
|
115
97
|
hash[key] = options[key] if options.key?(key)
|
116
|
-
hash
|
117
98
|
end
|
118
99
|
end
|
119
100
|
|
120
101
|
def upload_part_opts(options)
|
121
|
-
UPLOAD_PART_OPTIONS.
|
102
|
+
UPLOAD_PART_OPTIONS.each_with_object({}) do |key, hash|
|
122
103
|
hash[key] = options[key] if options.key?(key)
|
123
|
-
hash
|
124
104
|
end
|
125
105
|
end
|
126
106
|
|
127
107
|
def complete_opts(options)
|
128
|
-
COMPLETE_UPLOAD_OPTIONS.
|
108
|
+
COMPLETE_UPLOAD_OPTIONS.each_with_object({}) do |key, hash|
|
129
109
|
hash[key] = options[key] if options.key?(key)
|
130
|
-
hash
|
131
110
|
end
|
132
111
|
end
|
133
112
|
|
134
113
|
def read_to_part_body(read_pipe)
|
135
114
|
return if read_pipe.closed?
|
136
|
-
|
115
|
+
|
116
|
+
temp_io = @tempfile ? Tempfile.new('aws-sdk-s3-upload_stream') : StringIO.new(String.new)
|
137
117
|
temp_io.binmode
|
138
118
|
bytes_copied = IO.copy_stream(read_pipe, temp_io, @part_size)
|
139
119
|
temp_io.rewind
|
140
|
-
if bytes_copied
|
141
|
-
if Tempfile
|
120
|
+
if bytes_copied.zero?
|
121
|
+
if temp_io.is_a?(Tempfile)
|
142
122
|
temp_io.close
|
143
123
|
temp_io.unlink
|
144
124
|
end
|
@@ -151,51 +131,64 @@ module Aws
|
|
151
131
|
def upload_in_threads(read_pipe, completed, options, thread_errors)
|
152
132
|
mutex = Mutex.new
|
153
133
|
part_number = 0
|
154
|
-
@thread_count.times.map do
|
134
|
+
options.fetch(:thread_count, @thread_count).times.map do
|
155
135
|
thread = Thread.new do
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
[read_to_part_body(read_pipe), part_number += 1]
|
160
|
-
end
|
161
|
-
break unless (body || thread_part_number == 1)
|
162
|
-
begin
|
163
|
-
part = options.merge(
|
164
|
-
body: body,
|
165
|
-
part_number: thread_part_number,
|
166
|
-
)
|
167
|
-
resp = @client.upload_part(part)
|
168
|
-
completed_part = {etag: resp.etag, part_number: part[:part_number]}
|
169
|
-
|
170
|
-
# get the requested checksum from the response
|
171
|
-
if part[:checksum_algorithm]
|
172
|
-
k = "checksum_#{part[:checksum_algorithm].downcase}".to_sym
|
173
|
-
completed_part[k] = resp[k]
|
174
|
-
end
|
175
|
-
completed.push(completed_part)
|
176
|
-
ensure
|
177
|
-
if Tempfile === body
|
178
|
-
body.close
|
179
|
-
body.unlink
|
180
|
-
elsif StringIO === body
|
181
|
-
body.string.clear
|
182
|
-
end
|
183
|
-
end
|
136
|
+
loop do
|
137
|
+
body, thread_part_number = mutex.synchronize do
|
138
|
+
[read_to_part_body(read_pipe), part_number += 1]
|
184
139
|
end
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
140
|
+
break unless body || thread_part_number == 1
|
141
|
+
|
142
|
+
begin
|
143
|
+
part = options.merge(body: body, part_number: thread_part_number)
|
144
|
+
resp = @client.upload_part(part)
|
145
|
+
completed_part = create_completed_part(resp, part)
|
146
|
+
completed.push(completed_part)
|
147
|
+
ensure
|
148
|
+
clear_body(body)
|
191
149
|
end
|
192
|
-
error
|
193
150
|
end
|
151
|
+
nil
|
152
|
+
rescue StandardError => e
|
153
|
+
# keep other threads from uploading other parts
|
154
|
+
mutex.synchronize do
|
155
|
+
thread_errors.push(e)
|
156
|
+
read_pipe.close_read unless read_pipe.closed?
|
157
|
+
end
|
158
|
+
e
|
194
159
|
end
|
195
|
-
thread.abort_on_exception = true
|
196
160
|
thread
|
197
161
|
end
|
198
162
|
end
|
163
|
+
|
164
|
+
def create_completed_part(resp, part)
|
165
|
+
completed_part = { etag: resp.etag, part_number: part[:part_number] }
|
166
|
+
return completed_part unless part[:checksum_algorithm]
|
167
|
+
|
168
|
+
# get the requested checksum from the response
|
169
|
+
k = "checksum_#{part[:checksum_algorithm].downcase}".to_sym
|
170
|
+
completed_part[k] = resp[k]
|
171
|
+
completed_part
|
172
|
+
end
|
173
|
+
|
174
|
+
def ordered_parts(parts)
|
175
|
+
sorted = []
|
176
|
+
until parts.empty?
|
177
|
+
part = parts.pop
|
178
|
+
index = sorted.bsearch_index { |p| p[:part_number] >= part[:part_number] } || sorted.size
|
179
|
+
sorted.insert(index, part)
|
180
|
+
end
|
181
|
+
sorted
|
182
|
+
end
|
183
|
+
|
184
|
+
def clear_body(body)
|
185
|
+
if body.is_a?(Tempfile)
|
186
|
+
body.close
|
187
|
+
body.unlink
|
188
|
+
elsif body.is_a?(StringIO)
|
189
|
+
body.string.clear
|
190
|
+
end
|
191
|
+
end
|
199
192
|
end
|
200
193
|
end
|
201
194
|
end
|