aws-sdk-s3 1.13.0 → 1.14.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|