aws-sdk-s3 1.193.0 → 1.198.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -0
- data/VERSION +1 -1
- data/lib/aws-sdk-s3/client.rb +24 -17
- data/lib/aws-sdk-s3/customizations/object.rb +57 -76
- data/lib/aws-sdk-s3/customizations.rb +2 -1
- data/lib/aws-sdk-s3/file_downloader.rb +57 -74
- data/lib/aws-sdk-s3/file_uploader.rb +3 -5
- data/lib/aws-sdk-s3/multipart_download_error.rb +8 -0
- data/lib/aws-sdk-s3/multipart_file_uploader.rb +34 -65
- data/lib/aws-sdk-s3/multipart_stream_uploader.rb +80 -88
- data/lib/aws-sdk-s3/multipart_upload_error.rb +3 -4
- data/lib/aws-sdk-s3/transfer_manager.rb +246 -0
- data/lib/aws-sdk-s3.rb +1 -1
- data/sig/client.rbs +1 -0
- data/sig/resource.rbs +1 -0
- metadata +5 -3
@@ -7,7 +7,7 @@ module Aws
|
|
7
7
|
# @api private
|
8
8
|
class FileUploader
|
9
9
|
|
10
|
-
|
10
|
+
DEFAULT_MULTIPART_THRESHOLD = 100 * 1024 * 1024
|
11
11
|
|
12
12
|
# @param [Hash] options
|
13
13
|
# @option options [Client] :client
|
@@ -15,15 +15,13 @@ module Aws
|
|
15
15
|
def initialize(options = {})
|
16
16
|
@options = options
|
17
17
|
@client = options[:client] || Client.new
|
18
|
-
@multipart_threshold = options[:multipart_threshold] ||
|
19
|
-
ONE_HUNDRED_MEGABYTES
|
18
|
+
@multipart_threshold = options[:multipart_threshold] || DEFAULT_MULTIPART_THRESHOLD
|
20
19
|
end
|
21
20
|
|
22
21
|
# @return [Client]
|
23
22
|
attr_reader :client
|
24
23
|
|
25
|
-
# @return [Integer] Files larger than or equal to this in bytes are uploaded
|
26
|
-
# using a {MultipartFileUploader}.
|
24
|
+
# @return [Integer] Files larger than or equal to this in bytes are uploaded using a {MultipartFileUploader}.
|
27
25
|
attr_reader :multipart_threshold
|
28
26
|
|
29
27
|
# @param [String, Pathname, File, Tempfile] source The file to upload.
|
@@ -9,25 +9,11 @@ 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
|
-
Client.api.operation(:create_multipart_upload).input.shape.member_names
|
21
|
-
)
|
22
|
-
|
23
|
-
COMPLETE_OPTIONS = Set.new(
|
24
|
-
Client.api.operation(:complete_multipart_upload).input.shape.member_names
|
25
|
-
)
|
26
|
-
|
27
|
-
UPLOAD_PART_OPTIONS = Set.new(
|
28
|
-
Client.api.operation(:upload_part).input.shape.member_names
|
29
|
-
)
|
30
|
-
|
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)
|
31
17
|
CHECKSUM_KEYS = Set.new(
|
32
18
|
Client.api.operation(:upload_part).input.shape.members.map do |n, s|
|
33
19
|
n if s.location == 'header' && s.location_name.start_with?('x-amz-checksum-')
|
@@ -35,10 +21,10 @@ module Aws
|
|
35
21
|
)
|
36
22
|
|
37
23
|
# @option options [Client] :client
|
38
|
-
# @option options [Integer] :thread_count (
|
24
|
+
# @option options [Integer] :thread_count (DEFAULT_THREAD_COUNT)
|
39
25
|
def initialize(options = {})
|
40
26
|
@client = options[:client] || Client.new
|
41
|
-
@thread_count = options[:thread_count] ||
|
27
|
+
@thread_count = options[:thread_count] || DEFAULT_THREAD_COUNT
|
42
28
|
end
|
43
29
|
|
44
30
|
# @return [Client]
|
@@ -52,13 +38,11 @@ module Aws
|
|
52
38
|
# It will be invoked with [bytes_read], [total_sizes]
|
53
39
|
# @return [Seahorse::Client::Response] - the CompleteMultipartUploadResponse
|
54
40
|
def upload(source, options = {})
|
55
|
-
if File.size(source) < MIN_PART_SIZE
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
complete_upload(upload_id, parts, options)
|
61
|
-
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)
|
62
46
|
end
|
63
47
|
|
64
48
|
private
|
@@ -67,18 +51,21 @@ module Aws
|
|
67
51
|
@client.create_multipart_upload(create_opts(options)).upload_id
|
68
52
|
end
|
69
53
|
|
70
|
-
def complete_upload(upload_id, parts, options)
|
54
|
+
def complete_upload(upload_id, parts, source, options)
|
71
55
|
@client.complete_multipart_upload(
|
72
56
|
**complete_opts(options).merge(
|
73
57
|
upload_id: upload_id,
|
74
|
-
multipart_upload: { parts: parts }
|
58
|
+
multipart_upload: { parts: parts },
|
59
|
+
mpu_object_size: File.size(source)
|
75
60
|
)
|
76
61
|
)
|
62
|
+
rescue StandardError => e
|
63
|
+
abort_upload(upload_id, options, [e])
|
77
64
|
end
|
78
65
|
|
79
66
|
def upload_parts(upload_id, source, options)
|
80
|
-
pending = PartList.new(compute_parts(upload_id, source, options))
|
81
67
|
completed = PartList.new
|
68
|
+
pending = PartList.new(compute_parts(upload_id, source, options))
|
82
69
|
errors = upload_in_threads(pending, completed, options)
|
83
70
|
if errors.empty?
|
84
71
|
completed.to_a.sort_by { |part| part[:part_number] }
|
@@ -88,19 +75,15 @@ module Aws
|
|
88
75
|
end
|
89
76
|
|
90
77
|
def abort_upload(upload_id, options, errors)
|
91
|
-
@client.abort_multipart_upload(
|
92
|
-
bucket: options[:bucket],
|
93
|
-
key: options[:key],
|
94
|
-
upload_id: upload_id
|
95
|
-
)
|
78
|
+
@client.abort_multipart_upload(bucket: options[:bucket], key: options[:key], upload_id: upload_id)
|
96
79
|
msg = "multipart upload failed: #{errors.map(&:message).join('; ')}"
|
97
80
|
raise MultipartUploadError.new(msg, errors)
|
98
|
-
rescue MultipartUploadError =>
|
99
|
-
raise
|
100
|
-
rescue =>
|
101
|
-
msg = "failed to abort multipart upload: #{
|
81
|
+
rescue MultipartUploadError => e
|
82
|
+
raise e
|
83
|
+
rescue StandardError => e
|
84
|
+
msg = "failed to abort multipart upload: #{e.message}. "\
|
102
85
|
"Multipart upload failed: #{errors.map(&:message).join('; ')}"
|
103
|
-
raise MultipartUploadError.new(msg, errors + [
|
86
|
+
raise MultipartUploadError.new(msg, errors + [e])
|
104
87
|
end
|
105
88
|
|
106
89
|
def compute_parts(upload_id, source, options)
|
@@ -113,11 +96,7 @@ module Aws
|
|
113
96
|
parts << upload_part_opts(options).merge(
|
114
97
|
upload_id: upload_id,
|
115
98
|
part_number: part_number,
|
116
|
-
body: FilePart.new(
|
117
|
-
source: source,
|
118
|
-
offset: offset,
|
119
|
-
size: part_size(size, default_part_size, offset)
|
120
|
-
)
|
99
|
+
body: FilePart.new(source: source, offset: offset, size: part_size(size, default_part_size, offset))
|
121
100
|
)
|
122
101
|
part_number += 1
|
123
102
|
offset += default_part_size
|
@@ -136,28 +115,23 @@ module Aws
|
|
136
115
|
def create_opts(options)
|
137
116
|
opts = { checksum_algorithm: Aws::Plugins::ChecksumAlgorithm::DEFAULT_CHECKSUM }
|
138
117
|
opts[:checksum_type] = 'FULL_OBJECT' if has_checksum_key?(options.keys)
|
139
|
-
CREATE_OPTIONS.
|
118
|
+
CREATE_OPTIONS.each_with_object(opts) do |key, hash|
|
140
119
|
hash[key] = options[key] if options.key?(key)
|
141
|
-
hash
|
142
120
|
end
|
143
121
|
end
|
144
122
|
|
145
123
|
def complete_opts(options)
|
146
124
|
opts = {}
|
147
125
|
opts[:checksum_type] = 'FULL_OBJECT' if has_checksum_key?(options.keys)
|
148
|
-
COMPLETE_OPTIONS.
|
126
|
+
COMPLETE_OPTIONS.each_with_object(opts) do |key, hash|
|
149
127
|
hash[key] = options[key] if options.key?(key)
|
150
|
-
hash
|
151
128
|
end
|
152
129
|
end
|
153
130
|
|
154
131
|
def upload_part_opts(options)
|
155
|
-
UPLOAD_PART_OPTIONS.
|
156
|
-
|
157
|
-
|
158
|
-
hash[key] = options[key] unless checksum_key?(key)
|
159
|
-
end
|
160
|
-
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)
|
161
135
|
end
|
162
136
|
end
|
163
137
|
|
@@ -169,7 +143,7 @@ module Aws
|
|
169
143
|
options.fetch(:thread_count, @thread_count).times do
|
170
144
|
thread = Thread.new do
|
171
145
|
begin
|
172
|
-
while part = pending.shift
|
146
|
+
while (part = pending.shift)
|
173
147
|
if progress
|
174
148
|
part[:on_chunk_sent] =
|
175
149
|
proc do |_chunk, bytes, _total|
|
@@ -178,20 +152,17 @@ module Aws
|
|
178
152
|
end
|
179
153
|
resp = @client.upload_part(part)
|
180
154
|
part[:body].close
|
181
|
-
completed_part = {
|
182
|
-
etag: resp.etag,
|
183
|
-
part_number: part[:part_number]
|
184
|
-
}
|
155
|
+
completed_part = { etag: resp.etag, part_number: part[:part_number] }
|
185
156
|
algorithm = resp.context.params[:checksum_algorithm]
|
186
157
|
k = "checksum_#{algorithm.downcase}".to_sym
|
187
158
|
completed_part[k] = resp.send(k)
|
188
159
|
completed.push(completed_part)
|
189
160
|
end
|
190
161
|
nil
|
191
|
-
rescue =>
|
162
|
+
rescue StandardError => e
|
192
163
|
# keep other threads from uploading other parts
|
193
164
|
pending.clear!
|
194
|
-
|
165
|
+
e
|
195
166
|
end
|
196
167
|
end
|
197
168
|
threads << thread
|
@@ -213,7 +184,6 @@ module Aws
|
|
213
184
|
|
214
185
|
# @api private
|
215
186
|
class PartList
|
216
|
-
|
217
187
|
def initialize(parts = [])
|
218
188
|
@parts = parts
|
219
189
|
@mutex = Mutex.new
|
@@ -242,7 +212,6 @@ module Aws
|
|
242
212
|
def to_a
|
243
213
|
@mutex.synchronize { @parts.dup }
|
244
214
|
end
|
245
|
-
|
246
215
|
end
|
247
216
|
|
248
217
|
# @api private
|
@@ -261,4 +230,4 @@ module Aws
|
|
261
230
|
end
|
262
231
|
end
|
263
232
|
end
|
264
|
-
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,7 +29,7 @@ module Aws
|
|
43
29
|
|
44
30
|
# @option options [required,String] :bucket
|
45
31
|
# @option options [required,String] :key
|
46
|
-
# @option options [Integer] :thread_count (
|
32
|
+
# @option options [Integer] :thread_count (DEFAULT_THREAD_COUNT)
|
47
33
|
# @return [Seahorse::Client::Response] - the CompleteMultipartUploadResponse
|
48
34
|
def upload(options = {}, &block)
|
49
35
|
Aws::Plugins::UserAgent.metric('S3_TRANSFER') do
|
@@ -61,11 +47,10 @@ module Aws
|
|
61
47
|
|
62
48
|
def complete_upload(upload_id, parts, options)
|
63
49
|
@client.complete_multipart_upload(
|
64
|
-
**complete_opts(options).merge(
|
65
|
-
upload_id: upload_id,
|
66
|
-
multipart_upload: { parts: parts }
|
67
|
-
)
|
50
|
+
**complete_opts(options).merge(upload_id: upload_id, multipart_upload: { parts: parts })
|
68
51
|
)
|
52
|
+
rescue StandardError => e
|
53
|
+
abort_upload(upload_id, options, [e])
|
69
54
|
end
|
70
55
|
|
71
56
|
def upload_parts(upload_id, options, &block)
|
@@ -74,9 +59,11 @@ module Aws
|
|
74
59
|
errors = begin
|
75
60
|
IO.pipe do |read_pipe, write_pipe|
|
76
61
|
threads = upload_in_threads(
|
77
|
-
read_pipe,
|
62
|
+
read_pipe,
|
63
|
+
completed,
|
78
64
|
upload_part_opts(options).merge(upload_id: upload_id),
|
79
|
-
thread_errors
|
65
|
+
thread_errors
|
66
|
+
)
|
80
67
|
begin
|
81
68
|
block.call(write_pipe)
|
82
69
|
ensure
|
@@ -85,62 +72,53 @@ module Aws
|
|
85
72
|
end
|
86
73
|
threads.map(&:value).compact
|
87
74
|
end
|
88
|
-
rescue => e
|
75
|
+
rescue StandardError => e
|
89
76
|
thread_errors + [e]
|
90
77
|
end
|
78
|
+
return ordered_parts(completed) if errors.empty?
|
91
79
|
|
92
|
-
|
93
|
-
Array.new(completed.size) { completed.pop }.sort_by { |part| part[:part_number] }
|
94
|
-
else
|
95
|
-
abort_upload(upload_id, options, errors)
|
96
|
-
end
|
80
|
+
abort_upload(upload_id, options, errors)
|
97
81
|
end
|
98
82
|
|
99
83
|
def abort_upload(upload_id, options, errors)
|
100
|
-
@client.abort_multipart_upload(
|
101
|
-
bucket: options[:bucket],
|
102
|
-
key: options[:key],
|
103
|
-
upload_id: upload_id
|
104
|
-
)
|
84
|
+
@client.abort_multipart_upload(bucket: options[:bucket], key: options[:key], upload_id: upload_id)
|
105
85
|
msg = "multipart upload failed: #{errors.map(&:message).join('; ')}"
|
106
86
|
raise MultipartUploadError.new(msg, errors)
|
107
|
-
rescue MultipartUploadError =>
|
108
|
-
raise
|
109
|
-
rescue =>
|
110
|
-
msg = "failed to abort multipart upload: #{
|
87
|
+
rescue MultipartUploadError => e
|
88
|
+
raise e
|
89
|
+
rescue StandardError => e
|
90
|
+
msg = "failed to abort multipart upload: #{e.message}. "\
|
111
91
|
"Multipart upload failed: #{errors.map(&:message).join('; ')}"
|
112
|
-
raise MultipartUploadError.new(msg, errors + [
|
92
|
+
raise MultipartUploadError.new(msg, errors + [e])
|
113
93
|
end
|
114
94
|
|
115
95
|
def create_opts(options)
|
116
|
-
CREATE_OPTIONS.
|
96
|
+
CREATE_OPTIONS.each_with_object({}) do |key, hash|
|
117
97
|
hash[key] = options[key] if options.key?(key)
|
118
|
-
hash
|
119
98
|
end
|
120
99
|
end
|
121
100
|
|
122
101
|
def upload_part_opts(options)
|
123
|
-
UPLOAD_PART_OPTIONS.
|
102
|
+
UPLOAD_PART_OPTIONS.each_with_object({}) do |key, hash|
|
124
103
|
hash[key] = options[key] if options.key?(key)
|
125
|
-
hash
|
126
104
|
end
|
127
105
|
end
|
128
106
|
|
129
107
|
def complete_opts(options)
|
130
|
-
COMPLETE_UPLOAD_OPTIONS.
|
108
|
+
COMPLETE_UPLOAD_OPTIONS.each_with_object({}) do |key, hash|
|
131
109
|
hash[key] = options[key] if options.key?(key)
|
132
|
-
hash
|
133
110
|
end
|
134
111
|
end
|
135
112
|
|
136
113
|
def read_to_part_body(read_pipe)
|
137
114
|
return if read_pipe.closed?
|
138
|
-
|
115
|
+
|
116
|
+
temp_io = @tempfile ? Tempfile.new('aws-sdk-s3-upload_stream') : StringIO.new(String.new)
|
139
117
|
temp_io.binmode
|
140
118
|
bytes_copied = IO.copy_stream(read_pipe, temp_io, @part_size)
|
141
119
|
temp_io.rewind
|
142
|
-
if bytes_copied
|
143
|
-
if Tempfile
|
120
|
+
if bytes_copied.zero?
|
121
|
+
if temp_io.is_a?(Tempfile)
|
144
122
|
temp_io.close
|
145
123
|
temp_io.unlink
|
146
124
|
end
|
@@ -155,48 +133,62 @@ module Aws
|
|
155
133
|
part_number = 0
|
156
134
|
options.fetch(:thread_count, @thread_count).times.map do
|
157
135
|
thread = Thread.new do
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
[read_to_part_body(read_pipe), part_number += 1]
|
162
|
-
end
|
163
|
-
break unless (body || thread_part_number == 1)
|
164
|
-
begin
|
165
|
-
part = options.merge(
|
166
|
-
body: body,
|
167
|
-
part_number: thread_part_number,
|
168
|
-
)
|
169
|
-
resp = @client.upload_part(part)
|
170
|
-
completed_part = {etag: resp.etag, part_number: part[:part_number]}
|
171
|
-
|
172
|
-
# get the requested checksum from the response
|
173
|
-
if part[:checksum_algorithm]
|
174
|
-
k = "checksum_#{part[:checksum_algorithm].downcase}".to_sym
|
175
|
-
completed_part[k] = resp[k]
|
176
|
-
end
|
177
|
-
completed.push(completed_part)
|
178
|
-
ensure
|
179
|
-
if Tempfile === body
|
180
|
-
body.close
|
181
|
-
body.unlink
|
182
|
-
elsif StringIO === body
|
183
|
-
body.string.clear
|
184
|
-
end
|
185
|
-
end
|
136
|
+
loop do
|
137
|
+
body, thread_part_number = mutex.synchronize do
|
138
|
+
[read_to_part_body(read_pipe), part_number += 1]
|
186
139
|
end
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
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)
|
193
149
|
end
|
194
|
-
error
|
195
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
|
196
159
|
end
|
197
160
|
thread
|
198
161
|
end
|
199
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
|
200
192
|
end
|
201
193
|
end
|
202
194
|
end
|
@@ -2,17 +2,16 @@
|
|
2
2
|
|
3
3
|
module Aws
|
4
4
|
module S3
|
5
|
+
# Raise when multipart upload fails to complete.
|
5
6
|
class MultipartUploadError < StandardError
|
6
7
|
|
7
|
-
def initialize(message, errors)
|
8
|
+
def initialize(message, errors = [])
|
8
9
|
@errors = errors
|
9
10
|
super(message)
|
10
11
|
end
|
11
12
|
|
12
|
-
# @return [Array<StandardError>] The list of errors encountered
|
13
|
-
# when uploading or aborting the upload.
|
13
|
+
# @return [Array<StandardError>] The list of errors encountered when uploading or aborting the upload.
|
14
14
|
attr_reader :errors
|
15
|
-
|
16
15
|
end
|
17
16
|
end
|
18
17
|
end
|