aws-sdk-s3 1.13.0 → 1.14.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/lib/aws-sdk-s3.rb +1 -1
- data/lib/aws-sdk-s3/client.rb +1 -1
- data/lib/aws-sdk-s3/customizations.rb +1 -0
- data/lib/aws-sdk-s3/customizations/object.rb +48 -0
- data/lib/aws-sdk-s3/customizations/object_summary.rb +7 -0
- data/lib/aws-sdk-s3/multipart_stream_uploader.rb +160 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ebf985aabc5276f0257be4b38c436b5261e133aa
|
4
|
+
data.tar.gz: eb740e2166050fd5b48daf78d95797c167fc5d5b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5de1c034a92b407852bd6b268d42c3ecb8593f0d1c27ea0056e6b83f09263f42a4e8bfdfa3df18f01f0435bf1206a6f779c7d34e3c23d36a3e21510345a0da1e
|
7
|
+
data.tar.gz: 4a6d12d6dd43abb6064fd45b8ce703c98f46ffa51d220cd2b67a7f35e4ac1bcf00c0876874359f623db56b2f6ad95ff355b8669e967830bdd912a23868a7268f
|
data/lib/aws-sdk-s3.rb
CHANGED
data/lib/aws-sdk-s3/client.rb
CHANGED
@@ -6,6 +6,7 @@ require 'aws-sdk-s3/file_uploader'
|
|
6
6
|
require 'aws-sdk-s3/file_downloader'
|
7
7
|
require 'aws-sdk-s3/legacy_signer'
|
8
8
|
require 'aws-sdk-s3/multipart_file_uploader'
|
9
|
+
require 'aws-sdk-s3/multipart_stream_uploader'
|
9
10
|
require 'aws-sdk-s3/multipart_upload_error'
|
10
11
|
require 'aws-sdk-s3/object_copier'
|
11
12
|
require 'aws-sdk-s3/object_multipart_copier'
|
@@ -214,6 +214,54 @@ module Aws
|
|
214
214
|
url.to_s
|
215
215
|
end
|
216
216
|
|
217
|
+
# Uploads a stream in a streaming fashion to the current object in S3.
|
218
|
+
#
|
219
|
+
# # Passed chunks automatically split into multipart upload parts
|
220
|
+
# # and the parts are uploaded in parallel. This allows for streaming uploads
|
221
|
+
# # that never touch the disk.
|
222
|
+
#
|
223
|
+
# Note that this is known to have issues in JRuby until jruby-9.1.15.0, so avoid using this with older versions of JRuby.
|
224
|
+
#
|
225
|
+
# @example Streaming chunks of data
|
226
|
+
# obj.upload_file do |write_stream|
|
227
|
+
# 10.times { write_stream << 'hello' }
|
228
|
+
# end
|
229
|
+
#
|
230
|
+
# @option options [Integer] :thread_count
|
231
|
+
# The number of parallel multipart uploads
|
232
|
+
# Default `:thread_count` is `10`.
|
233
|
+
#
|
234
|
+
# @option options [Boolean] :tempfile
|
235
|
+
# Normally read data is stored in memory when building the parts in order to complete
|
236
|
+
# the underlying multipart upload. By passing `:tempfile => true` data read will be
|
237
|
+
# temporarily stored on disk reducing the memory footprint vastly.
|
238
|
+
# Default `:tempfile` is `false`.
|
239
|
+
#
|
240
|
+
# @option options [Integer] :part_size
|
241
|
+
# Define how big each part size but the last should be.
|
242
|
+
# Default `:part_size` is `5 * 1024 * 1024`.
|
243
|
+
#
|
244
|
+
# @raise [MultipartUploadError] If an object is being uploaded in
|
245
|
+
# parts, and the upload can not be completed, then the upload is
|
246
|
+
# aborted and this error is raised. The raised error has a `#errors`
|
247
|
+
# method that returns the failures that caused the upload to be
|
248
|
+
# aborted.
|
249
|
+
#
|
250
|
+
# @return [Boolean] Returns `true` when the object is uploaded
|
251
|
+
# without any errors.
|
252
|
+
#
|
253
|
+
def upload_stream(options = {}, &block)
|
254
|
+
uploading_options = options.dup
|
255
|
+
uploader = MultipartStreamUploader.new(
|
256
|
+
client: client,
|
257
|
+
thread_count: uploading_options.delete(:thread_count),
|
258
|
+
tempfile: uploading_options.delete(:tempfile),
|
259
|
+
part_size: uploading_options.delete(:part_size),
|
260
|
+
)
|
261
|
+
uploader.upload(uploading_options.merge(bucket: bucket_name, key: key), &block)
|
262
|
+
true
|
263
|
+
end
|
264
|
+
|
217
265
|
# Uploads a file from disk to the current object in S3.
|
218
266
|
#
|
219
267
|
# # small files are uploaded in a single API call
|
@@ -60,6 +60,13 @@ module Aws
|
|
60
60
|
object.upload_file(source, options)
|
61
61
|
end
|
62
62
|
|
63
|
+
# @options (see Object#upload_stream)
|
64
|
+
# @return (see Object#upload_stream)
|
65
|
+
# @see Object#upload_stream
|
66
|
+
def upload_stream(options = {}, &block)
|
67
|
+
object.upload_stream(options, &block)
|
68
|
+
end
|
69
|
+
|
63
70
|
# @param (see Object#download_file)
|
64
71
|
# @options (see Object#download_file)
|
65
72
|
# @return (see Object#download_file)
|
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'set'
|
3
|
+
require 'tempfile'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
module Aws
|
7
|
+
module S3
|
8
|
+
# @api private
|
9
|
+
class MultipartStreamUploader
|
10
|
+
# api private
|
11
|
+
PART_SIZE = 5 * 1024 * 1024 # 5MB
|
12
|
+
|
13
|
+
# api private
|
14
|
+
THREAD_COUNT = 10
|
15
|
+
|
16
|
+
# api private
|
17
|
+
TEMPFILE_PREIX = 'aws-sdk-s3-upload_stream'.freeze
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
CREATE_OPTIONS =
|
21
|
+
Set.new(Client.api.operation(:create_multipart_upload).input.shape.member_names)
|
22
|
+
|
23
|
+
# @api private
|
24
|
+
UPLOAD_PART_OPTIONS =
|
25
|
+
Set.new(Client.api.operation(:upload_part).input.shape.member_names)
|
26
|
+
|
27
|
+
# @option options [Client] :client
|
28
|
+
def initialize(options = {})
|
29
|
+
@client = options[:client] || Client.new
|
30
|
+
@tempfile = options[:tempfile]
|
31
|
+
@part_size = options[:part_size] || PART_SIZE
|
32
|
+
@thread_count = options[:thread_count] || THREAD_COUNT
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Client]
|
36
|
+
attr_reader :client
|
37
|
+
|
38
|
+
# @option options [required,String] :bucket
|
39
|
+
# @option options [required,String] :key
|
40
|
+
# @return [void]
|
41
|
+
def upload(options = {}, &block)
|
42
|
+
upload_id = initiate_upload(options)
|
43
|
+
parts = upload_parts(upload_id, options, &block)
|
44
|
+
complete_upload(upload_id, parts, options)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def initiate_upload(options)
|
50
|
+
@client.create_multipart_upload(create_opts(options)).upload_id
|
51
|
+
end
|
52
|
+
|
53
|
+
def complete_upload(upload_id, parts, options)
|
54
|
+
@client.complete_multipart_upload(
|
55
|
+
bucket: options[:bucket],
|
56
|
+
key: options[:key],
|
57
|
+
upload_id: upload_id,
|
58
|
+
multipart_upload: { parts: parts })
|
59
|
+
end
|
60
|
+
|
61
|
+
def upload_parts(upload_id, options, &block)
|
62
|
+
completed = Queue.new
|
63
|
+
errors = IO.pipe do |read_pipe, write_pipe|
|
64
|
+
threads = upload_in_threads(read_pipe, completed, upload_part_opts(options).merge(upload_id: upload_id))
|
65
|
+
block.call(write_pipe)
|
66
|
+
write_pipe.close
|
67
|
+
threads.map(&:value).compact
|
68
|
+
end
|
69
|
+
if errors.empty?
|
70
|
+
Array.new(completed.size) { completed.pop }.sort_by { |part| part[:part_number] }
|
71
|
+
else
|
72
|
+
abort_upload(upload_id, options, errors)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def abort_upload(upload_id, options, errors)
|
77
|
+
@client.abort_multipart_upload(
|
78
|
+
bucket: options[:bucket],
|
79
|
+
key: options[:key],
|
80
|
+
upload_id: upload_id
|
81
|
+
)
|
82
|
+
msg = "multipart upload failed: #{errors.map(&:message).join("; ")}"
|
83
|
+
raise MultipartUploadError.new(msg, errors)
|
84
|
+
rescue MultipartUploadError => error
|
85
|
+
raise error
|
86
|
+
rescue => error
|
87
|
+
msg = "failed to abort multipart upload: #{error.message}"
|
88
|
+
raise MultipartUploadError.new(msg, errors + [error])
|
89
|
+
end
|
90
|
+
|
91
|
+
def create_opts(options)
|
92
|
+
CREATE_OPTIONS.inject({}) do |hash, key|
|
93
|
+
hash[key] = options[key] if options.key?(key)
|
94
|
+
hash
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def upload_part_opts(options)
|
99
|
+
UPLOAD_PART_OPTIONS.inject({}) do |hash, key|
|
100
|
+
hash[key] = options[key] if options.key?(key)
|
101
|
+
hash
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def read_to_part_body(read_pipe)
|
106
|
+
return if read_pipe.closed?
|
107
|
+
temp_io = @tempfile ? Tempfile.new(TEMPFILE_PREIX) : StringIO.new
|
108
|
+
temp_io.binmode
|
109
|
+
bytes_copied = IO.copy_stream(read_pipe, temp_io, @part_size)
|
110
|
+
temp_io.rewind
|
111
|
+
if bytes_copied == 0
|
112
|
+
if Tempfile === temp_io
|
113
|
+
temp_io.close
|
114
|
+
temp_io.unlink
|
115
|
+
end
|
116
|
+
nil
|
117
|
+
else
|
118
|
+
temp_io
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def upload_in_threads(read_pipe, completed, options)
|
123
|
+
mutex = Mutex.new
|
124
|
+
part_number = 0
|
125
|
+
@thread_count.times.map do
|
126
|
+
thread = Thread.new do
|
127
|
+
begin
|
128
|
+
loop do
|
129
|
+
body, thread_part_number = mutex.synchronize do
|
130
|
+
[read_to_part_body(read_pipe), part_number += 1]
|
131
|
+
end
|
132
|
+
break unless body
|
133
|
+
begin
|
134
|
+
part = options.merge(
|
135
|
+
body: body,
|
136
|
+
part_number: thread_part_number,
|
137
|
+
)
|
138
|
+
resp = @client.upload_part(part)
|
139
|
+
completed << {etag: resp.etag, part_number: part[:part_number]}
|
140
|
+
ensure
|
141
|
+
if Tempfile === body
|
142
|
+
body.close
|
143
|
+
body.unlink
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
nil
|
148
|
+
rescue => error
|
149
|
+
# keep other threads from uploading other parts
|
150
|
+
mutex.synchronize { read_pipe.close_read }
|
151
|
+
error
|
152
|
+
end
|
153
|
+
end
|
154
|
+
thread.abort_on_exception = true
|
155
|
+
thread
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
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.
|
4
|
+
version: 1.14.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: 2018-
|
11
|
+
date: 2018-06-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-kms
|
@@ -109,6 +109,7 @@ files:
|
|
109
109
|
- lib/aws-sdk-s3/file_uploader.rb
|
110
110
|
- lib/aws-sdk-s3/legacy_signer.rb
|
111
111
|
- lib/aws-sdk-s3/multipart_file_uploader.rb
|
112
|
+
- lib/aws-sdk-s3/multipart_stream_uploader.rb
|
112
113
|
- lib/aws-sdk-s3/multipart_upload.rb
|
113
114
|
- lib/aws-sdk-s3/multipart_upload_error.rb
|
114
115
|
- lib/aws-sdk-s3/multipart_upload_part.rb
|