aws-sdk-s3 1.132.1 → 1.133.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/VERSION +1 -1
- data/lib/aws-sdk-s3/client.rb +1 -1
- data/lib/aws-sdk-s3/customizations/object.rb +42 -0
- data/lib/aws-sdk-s3/file_downloader.rb +113 -23
- data/lib/aws-sdk-s3.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 04f11cfde483787571492064565bb589b16323f4bb7f33f5a7507b993f6cead3
|
4
|
+
data.tar.gz: 1dd900a34a20b97fb82c41407ca12c40d33397fb14e29678e0f7e3b0a1e5b77c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 745b68d37b0845463f3c2a1cfdb29e8833bd0d723e31a1071c1f3936f095e4c735c909573c8df49f077b3f56dfa13f9e45552c68d7e5a5c0718b67d3f08418e2
|
7
|
+
data.tar.gz: f995dde0e5628863ab664b36a4db27a23e8d68827aab268fecacfa217a104f1bb3cfd67e84bf6729e52544d9015fd3c64765a53fe0758647e5904c1f104235e1
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
Unreleased Changes
|
2
2
|
------------------
|
3
3
|
|
4
|
+
1.133.0 (2023-08-22)
|
5
|
+
------------------
|
6
|
+
|
7
|
+
* Feature - Code Generated Changes, see `./build_tools` or `aws-sdk-core`'s CHANGELOG.md for details.
|
8
|
+
|
9
|
+
* Feature - Add support for `progress_callback` in `Object#download_file` and improve multi-threaded performance #(2901).
|
10
|
+
|
4
11
|
1.132.1 (2023-08-09)
|
5
12
|
------------------
|
6
13
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.133.0
|
data/lib/aws-sdk-s3/client.rb
CHANGED
@@ -15653,7 +15653,7 @@ module Aws::S3
|
|
15653
15653
|
params: params,
|
15654
15654
|
config: config)
|
15655
15655
|
context[:gem_name] = 'aws-sdk-s3'
|
15656
|
-
context[:gem_version] = '1.
|
15656
|
+
context[:gem_version] = '1.133.0'
|
15657
15657
|
Seahorse::Client::Request.new(handlers, context)
|
15658
15658
|
end
|
15659
15659
|
|
@@ -353,6 +353,10 @@ module Aws
|
|
353
353
|
# obj.upload_stream do |write_stream|
|
354
354
|
# IO.copy_stream(STDIN, write_stream)
|
355
355
|
# end
|
356
|
+
# @param [Hash] options
|
357
|
+
# Additional options for {Client#create_multipart_upload},
|
358
|
+
# {Client#complete_multipart_upload},
|
359
|
+
# and {Client#upload_part} can be provided.
|
356
360
|
#
|
357
361
|
# @option options [Integer] :thread_count (10) The number of parallel
|
358
362
|
# multipart uploads
|
@@ -375,6 +379,9 @@ module Aws
|
|
375
379
|
# @return [Boolean] Returns `true` when the object is uploaded
|
376
380
|
# without any errors.
|
377
381
|
#
|
382
|
+
# @see Client#create_multipart_upload
|
383
|
+
# @see Client#complete_multipart_upload
|
384
|
+
# @see Client#upload_part
|
378
385
|
def upload_stream(options = {}, &block)
|
379
386
|
uploading_options = options.dup
|
380
387
|
uploader = MultipartStreamUploader.new(
|
@@ -427,6 +434,13 @@ module Aws
|
|
427
434
|
# using an open Tempfile, rewind it before uploading or else the object
|
428
435
|
# will be empty.
|
429
436
|
#
|
437
|
+
# @param [Hash] options
|
438
|
+
# Additional options for {Client#put_object}
|
439
|
+
# when file sizes below the multipart threshold. For files larger than
|
440
|
+
# the multipart threshold, options for {Client#create_multipart_upload},
|
441
|
+
# {Client#complete_multipart_upload},
|
442
|
+
# and {Client#upload_part} can be provided.
|
443
|
+
#
|
430
444
|
# @option options [Integer] :multipart_threshold (104857600) Files larger
|
431
445
|
# than or equal to `:multipart_threshold` are uploaded using the S3
|
432
446
|
# multipart APIs.
|
@@ -448,6 +462,11 @@ module Aws
|
|
448
462
|
#
|
449
463
|
# @return [Boolean] Returns `true` when the object is uploaded
|
450
464
|
# without any errors.
|
465
|
+
#
|
466
|
+
# @see Client#put_object
|
467
|
+
# @see Client#create_multipart_upload
|
468
|
+
# @see Client#complete_multipart_upload
|
469
|
+
# @see Client#upload_part
|
451
470
|
def upload_file(source, options = {})
|
452
471
|
uploading_options = options.dup
|
453
472
|
uploader = FileUploader.new(
|
@@ -475,8 +494,21 @@ module Aws
|
|
475
494
|
# # and the parts are downloaded in parallel
|
476
495
|
# obj.download_file('/path/to/very_large_file')
|
477
496
|
#
|
497
|
+
# You can provide a callback to monitor progress of the download:
|
498
|
+
#
|
499
|
+
# # bytes and part_sizes are each an array with 1 entry per part
|
500
|
+
# # part_sizes may not be known until the first bytes are retrieved
|
501
|
+
# progress = Proc.new do |bytes, part_sizes, file_size|
|
502
|
+
# puts bytes.map.with_index { |b, i| "Part #{i+1}: #{b} / #{part_sizes[i]}"}.join(' ') + "Total: #{100.0 * bytes.sum / file_size}%" }
|
503
|
+
# end
|
504
|
+
# obj.download_file('/path/to/file', progress_callback: progress)
|
505
|
+
#
|
478
506
|
# @param [String] destination Where to download the file to.
|
479
507
|
#
|
508
|
+
# @param [Hash] options
|
509
|
+
# Additional options for {Client#get_object} and #{Client#head_object}
|
510
|
+
# may be provided.
|
511
|
+
#
|
480
512
|
# @option options [String] mode `auto`, `single_request`, `get_range`
|
481
513
|
# `single_request` mode forces only 1 GET request is made in download,
|
482
514
|
# `get_range` mode allows `chunk_size` parameter to configured in
|
@@ -505,8 +537,18 @@ module Aws
|
|
505
537
|
# response. For multipart downloads, this will be called for each
|
506
538
|
# part that is downloaded and validated.
|
507
539
|
#
|
540
|
+
# @option options [Proc] :progress_callback
|
541
|
+
# A Proc that will be called when each chunk of the download is received.
|
542
|
+
# It will be invoked with [bytes_read], [part_sizes], file_size.
|
543
|
+
# When the object is downloaded as parts (rather than by ranges), the
|
544
|
+
# part_sizes will not be known ahead of time and will be nil in the
|
545
|
+
# callback until the first bytes in the part are received.
|
546
|
+
#
|
508
547
|
# @return [Boolean] Returns `true` when the file is downloaded without
|
509
548
|
# any errors.
|
549
|
+
#
|
550
|
+
# @see Client#get_object
|
551
|
+
# @see Client#head_object
|
510
552
|
def download_file(destination, options = {})
|
511
553
|
downloader = FileDownloader.new(client: client)
|
512
554
|
Aws::Plugins::UserAgent.feature('resource') do
|
@@ -40,6 +40,8 @@ module Aws
|
|
40
40
|
end
|
41
41
|
@on_checksum_validated = options[:on_checksum_validated]
|
42
42
|
|
43
|
+
@progress_callback = options[:progress_callback]
|
44
|
+
|
43
45
|
validate!
|
44
46
|
|
45
47
|
Aws::Plugins::UserAgent.feature('s3-transfer') do
|
@@ -49,7 +51,7 @@ module Aws
|
|
49
51
|
when 'get_range'
|
50
52
|
if @chunk_size
|
51
53
|
resp = @client.head_object(@params)
|
52
|
-
multithreaded_get_by_ranges(
|
54
|
+
multithreaded_get_by_ranges(resp.content_length)
|
53
55
|
else
|
54
56
|
msg = 'In :get_range mode, :chunk_size must be provided'
|
55
57
|
raise ArgumentError, msg
|
@@ -82,7 +84,7 @@ module Aws
|
|
82
84
|
if resp.content_length <= MIN_CHUNK_SIZE
|
83
85
|
single_request
|
84
86
|
else
|
85
|
-
multithreaded_get_by_ranges(
|
87
|
+
multithreaded_get_by_ranges(resp.content_length)
|
86
88
|
end
|
87
89
|
else
|
88
90
|
# partNumber is an option
|
@@ -99,9 +101,9 @@ module Aws
|
|
99
101
|
chunk_size = compute_chunk(file_size)
|
100
102
|
part_size = (file_size.to_f / count.to_f).ceil
|
101
103
|
if chunk_size < part_size
|
102
|
-
multithreaded_get_by_ranges(
|
104
|
+
multithreaded_get_by_ranges(file_size)
|
103
105
|
else
|
104
|
-
multithreaded_get_by_parts(count)
|
106
|
+
multithreaded_get_by_parts(count, file_size)
|
105
107
|
end
|
106
108
|
end
|
107
109
|
|
@@ -133,30 +135,65 @@ module Aws
|
|
133
135
|
chunks.each_slice(@thread_count).to_a
|
134
136
|
end
|
135
137
|
|
136
|
-
def multithreaded_get_by_ranges(
|
137
|
-
|
138
|
+
def multithreaded_get_by_ranges(file_size)
|
139
|
+
offset = 0
|
140
|
+
default_chunk_size = compute_chunk(file_size)
|
141
|
+
chunks = []
|
142
|
+
part_number = 1 # parts start at 1
|
143
|
+
while offset < file_size
|
144
|
+
progress = offset + default_chunk_size
|
145
|
+
progress = file_size if progress > file_size
|
146
|
+
range = "bytes=#{offset}-#{progress - 1}"
|
147
|
+
chunks << Part.new(
|
148
|
+
part_number: part_number,
|
149
|
+
size: (progress-offset),
|
150
|
+
params: @params.merge(range: range)
|
151
|
+
)
|
152
|
+
part_number += 1
|
153
|
+
offset = progress
|
154
|
+
end
|
155
|
+
download_in_threads(PartList.new(chunks), file_size)
|
138
156
|
end
|
139
157
|
|
140
|
-
def multithreaded_get_by_parts(
|
141
|
-
|
158
|
+
def multithreaded_get_by_parts(n_parts, total_size)
|
159
|
+
parts = (1..n_parts).map do |part|
|
160
|
+
Part.new(part_number: part, params: @params.merge(part_number: part))
|
161
|
+
end
|
162
|
+
download_in_threads(PartList.new(parts), total_size)
|
142
163
|
end
|
143
164
|
|
144
|
-
def
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
165
|
+
def download_in_threads(pending, total_size)
|
166
|
+
threads = []
|
167
|
+
if @progress_callback
|
168
|
+
progress = MultipartProgress.new(pending, total_size, @progress_callback)
|
169
|
+
end
|
170
|
+
@thread_count.times do
|
171
|
+
thread = Thread.new do
|
172
|
+
begin
|
173
|
+
while part = pending.shift
|
174
|
+
if progress
|
175
|
+
part.params[:on_chunk_received] =
|
176
|
+
proc do |_chunk, bytes, total|
|
177
|
+
progress.call(part.part_number, bytes, total)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
resp = @client.get_object(part.params)
|
181
|
+
write(resp)
|
182
|
+
if @on_checksum_validated && resp.checksum_validated
|
183
|
+
@on_checksum_validated.call(resp.checksum_validated, resp)
|
184
|
+
end
|
155
185
|
end
|
186
|
+
nil
|
187
|
+
rescue => error
|
188
|
+
# keep other threads from downloading other parts
|
189
|
+
pending.clear!
|
190
|
+
raise error
|
156
191
|
end
|
157
192
|
end
|
158
|
-
|
193
|
+
thread.abort_on_exception = true
|
194
|
+
threads << thread
|
159
195
|
end
|
196
|
+
threads.map(&:value).compact
|
160
197
|
end
|
161
198
|
|
162
199
|
def write(resp)
|
@@ -166,9 +203,9 @@ module Aws
|
|
166
203
|
end
|
167
204
|
|
168
205
|
def single_request
|
169
|
-
|
170
|
-
|
171
|
-
)
|
206
|
+
params = @params.merge(response_target: @path)
|
207
|
+
params[:on_chunk_received] = single_part_progress if @progress_callback
|
208
|
+
resp = @client.get_object(params)
|
172
209
|
|
173
210
|
return resp unless @on_checksum_validated
|
174
211
|
|
@@ -178,6 +215,59 @@ module Aws
|
|
178
215
|
|
179
216
|
resp
|
180
217
|
end
|
218
|
+
|
219
|
+
def single_part_progress
|
220
|
+
proc do |_chunk, bytes_read, total_size|
|
221
|
+
@progress_callback.call([bytes_read], [total_size], total_size)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
class Part < Struct.new(:part_number, :size, :params)
|
226
|
+
include Aws::Structure
|
227
|
+
end
|
228
|
+
|
229
|
+
# @api private
|
230
|
+
class PartList
|
231
|
+
include Enumerable
|
232
|
+
def initialize(parts = [])
|
233
|
+
@parts = parts
|
234
|
+
@mutex = Mutex.new
|
235
|
+
end
|
236
|
+
|
237
|
+
def shift
|
238
|
+
@mutex.synchronize { @parts.shift }
|
239
|
+
end
|
240
|
+
|
241
|
+
def size
|
242
|
+
@mutex.synchronize { @parts.size }
|
243
|
+
end
|
244
|
+
|
245
|
+
def clear!
|
246
|
+
@mutex.synchronize { @parts.clear }
|
247
|
+
end
|
248
|
+
|
249
|
+
def each(&block)
|
250
|
+
@mutex.synchronize { @parts.each(&block) }
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# @api private
|
255
|
+
class MultipartProgress
|
256
|
+
def initialize(parts, total_size, progress_callback)
|
257
|
+
@bytes_received = Array.new(parts.size, 0)
|
258
|
+
@part_sizes = parts.map(&:size)
|
259
|
+
@total_size = total_size
|
260
|
+
@progress_callback = progress_callback
|
261
|
+
end
|
262
|
+
|
263
|
+
def call(part_number, bytes_received, total)
|
264
|
+
# part numbers start at 1
|
265
|
+
@bytes_received[part_number - 1] = bytes_received
|
266
|
+
# part size may not be known until we get the first response
|
267
|
+
@part_sizes[part_number - 1] ||= total
|
268
|
+
@progress_callback.call(@bytes_received, @part_sizes, @total_size)
|
269
|
+
end
|
270
|
+
end
|
181
271
|
end
|
182
272
|
end
|
183
273
|
end
|
data/lib/aws-sdk-s3.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aws-sdk-s3
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.133.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Amazon Web Services
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-08-
|
11
|
+
date: 2023-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-kms
|
@@ -47,7 +47,7 @@ dependencies:
|
|
47
47
|
version: '3'
|
48
48
|
- - ">="
|
49
49
|
- !ruby/object:Gem::Version
|
50
|
-
version: 3.
|
50
|
+
version: 3.181.0
|
51
51
|
type: :runtime
|
52
52
|
prerelease: false
|
53
53
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -57,7 +57,7 @@ dependencies:
|
|
57
57
|
version: '3'
|
58
58
|
- - ">="
|
59
59
|
- !ruby/object:Gem::Version
|
60
|
-
version: 3.
|
60
|
+
version: 3.181.0
|
61
61
|
description: Official AWS Ruby gem for Amazon Simple Storage Service (Amazon S3).
|
62
62
|
This gem is part of the AWS SDK for Ruby.
|
63
63
|
email:
|