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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3e9c124873324a9bbaa9f9486830a64940d0b0eef231e64a411ff9b324c740be
4
- data.tar.gz: cf26234ca89937e9aa39e44be5ad26b85902ad2eca5f1dbfa9fccf32a93e917e
3
+ metadata.gz: 04f11cfde483787571492064565bb589b16323f4bb7f33f5a7507b993f6cead3
4
+ data.tar.gz: 1dd900a34a20b97fb82c41407ca12c40d33397fb14e29678e0f7e3b0a1e5b77c
5
5
  SHA512:
6
- metadata.gz: 534772057f8571cdb4ce8e6ba47350b672723cc37f654cd5befa1e426c59eb12b7ddb51620ed20dc513c7e4b16dfab54507c261ad8124ca1abe841a1573e1b9f
7
- data.tar.gz: ab4ebcde9d217b7e14c7f94696793ece9a2c5c74921e5e087f156585ffd76d9536776054a1a76e9b5965576d41bd9a1d0efc20fddda1aa472272f15d71e48322
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.132.1
1
+ 1.133.0
@@ -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.132.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(construct_chunks(resp.content_length))
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(construct_chunks(resp.content_length))
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(construct_chunks(file_size))
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(chunks)
137
- thread_batches(chunks, 'range')
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(parts)
141
- thread_batches(parts, 'part_number')
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 thread_batches(chunks, param)
145
- batches(chunks, param).each do |batch|
146
- threads = []
147
- batch.each do |chunk|
148
- threads << Thread.new do
149
- resp = @client.get_object(
150
- @params.merge(param.to_sym => chunk)
151
- )
152
- write(resp)
153
- if @on_checksum_validated && resp.checksum_validated
154
- @on_checksum_validated.call(resp.checksum_validated, resp)
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
- threads.each(&:join)
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
- resp = @client.get_object(
170
- @params.merge(response_target: @path)
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
@@ -73,6 +73,6 @@ require_relative 'aws-sdk-s3/event_streams'
73
73
  # @!group service
74
74
  module Aws::S3
75
75
 
76
- GEM_VERSION = '1.132.1'
76
+ GEM_VERSION = '1.133.0'
77
77
 
78
78
  end
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.132.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-09 00:00:00.000000000 Z
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.179.0
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.179.0
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: