aws-sdk-s3 1.21.0 → 1.117.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +930 -0
- data/LICENSE.txt +202 -0
- data/VERSION +1 -0
- data/lib/aws-sdk-s3/bucket.rb +393 -75
- data/lib/aws-sdk-s3/bucket_acl.rb +57 -14
- data/lib/aws-sdk-s3/bucket_cors.rb +67 -13
- data/lib/aws-sdk-s3/bucket_lifecycle.rb +54 -15
- data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +56 -15
- data/lib/aws-sdk-s3/bucket_logging.rb +52 -15
- data/lib/aws-sdk-s3/bucket_notification.rb +47 -17
- data/lib/aws-sdk-s3/bucket_policy.rb +51 -13
- data/lib/aws-sdk-s3/bucket_region_cache.rb +2 -0
- data/lib/aws-sdk-s3/bucket_request_payment.rb +51 -12
- data/lib/aws-sdk-s3/bucket_tagging.rb +59 -13
- data/lib/aws-sdk-s3/bucket_versioning.rb +118 -12
- data/lib/aws-sdk-s3/bucket_website.rb +66 -13
- data/lib/aws-sdk-s3/client.rb +11422 -2518
- data/lib/aws-sdk-s3/client_api.rb +1196 -155
- data/lib/aws-sdk-s3/customizations/bucket.rb +53 -36
- data/lib/aws-sdk-s3/customizations/multipart_upload.rb +2 -0
- data/lib/aws-sdk-s3/customizations/object.rb +200 -62
- data/lib/aws-sdk-s3/customizations/object_summary.rb +5 -0
- data/lib/aws-sdk-s3/customizations/types/list_object_versions_output.rb +2 -0
- data/lib/aws-sdk-s3/customizations.rb +4 -1
- data/lib/aws-sdk-s3/encryption/client.rb +23 -6
- data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +71 -29
- data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +43 -5
- data/lib/aws-sdk-s3/encryption/default_key_provider.rb +2 -0
- data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +13 -2
- data/lib/aws-sdk-s3/encryption/errors.rb +2 -0
- data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +11 -3
- data/lib/aws-sdk-s3/encryption/io_decrypter.rb +11 -3
- data/lib/aws-sdk-s3/encryption/io_encrypter.rb +2 -0
- data/lib/aws-sdk-s3/encryption/key_provider.rb +2 -0
- data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +34 -3
- data/lib/aws-sdk-s3/encryption/materials.rb +8 -6
- data/lib/aws-sdk-s3/encryption/utils.rb +25 -0
- data/lib/aws-sdk-s3/encryption.rb +4 -0
- data/lib/aws-sdk-s3/encryptionV2/client.rb +566 -0
- data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +222 -0
- data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +170 -0
- data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +40 -0
- data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +65 -0
- data/lib/aws-sdk-s3/encryptionV2/errors.rb +37 -0
- data/lib/aws-sdk-s3/encryptionV2/io_auth_decrypter.rb +58 -0
- data/lib/aws-sdk-s3/encryptionV2/io_decrypter.rb +37 -0
- data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +73 -0
- data/lib/aws-sdk-s3/encryptionV2/key_provider.rb +31 -0
- data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +169 -0
- data/lib/aws-sdk-s3/encryptionV2/materials.rb +60 -0
- data/lib/aws-sdk-s3/encryptionV2/utils.rb +103 -0
- data/lib/aws-sdk-s3/encryption_v2.rb +23 -0
- data/lib/aws-sdk-s3/endpoint_parameters.rb +142 -0
- data/lib/aws-sdk-s3/endpoint_provider.rb +2020 -0
- data/lib/aws-sdk-s3/endpoints.rb +2149 -0
- data/lib/aws-sdk-s3/errors.rb +123 -1
- data/lib/aws-sdk-s3/event_streams.rb +20 -7
- data/lib/aws-sdk-s3/file_downloader.rb +17 -10
- data/lib/aws-sdk-s3/file_part.rb +11 -6
- data/lib/aws-sdk-s3/file_uploader.rb +33 -14
- data/lib/aws-sdk-s3/legacy_signer.rb +17 -25
- data/lib/aws-sdk-s3/multipart_file_uploader.rb +78 -19
- data/lib/aws-sdk-s3/multipart_stream_uploader.rb +54 -15
- data/lib/aws-sdk-s3/multipart_upload.rb +178 -28
- data/lib/aws-sdk-s3/multipart_upload_error.rb +2 -0
- data/lib/aws-sdk-s3/multipart_upload_part.rb +237 -44
- data/lib/aws-sdk-s3/object.rb +897 -154
- data/lib/aws-sdk-s3/object_acl.rb +81 -20
- data/lib/aws-sdk-s3/object_copier.rb +2 -0
- data/lib/aws-sdk-s3/object_multipart_copier.rb +2 -0
- data/lib/aws-sdk-s3/object_summary.rb +649 -139
- data/lib/aws-sdk-s3/object_version.rb +167 -65
- data/lib/aws-sdk-s3/plugins/accelerate.rb +17 -64
- data/lib/aws-sdk-s3/plugins/arn.rb +70 -0
- data/lib/aws-sdk-s3/plugins/bucket_dns.rb +7 -43
- data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +20 -3
- data/lib/aws-sdk-s3/plugins/dualstack.rb +7 -50
- data/lib/aws-sdk-s3/plugins/endpoints.rb +262 -0
- data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +5 -4
- data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +3 -1
- data/lib/aws-sdk-s3/plugins/http_200_errors.rb +11 -3
- data/lib/aws-sdk-s3/plugins/iad_regional_endpoint.rb +44 -0
- data/lib/aws-sdk-s3/plugins/location_constraint.rb +2 -0
- data/lib/aws-sdk-s3/plugins/md5s.rb +34 -27
- data/lib/aws-sdk-s3/plugins/redirects.rb +2 -0
- data/lib/aws-sdk-s3/plugins/s3_host_id.rb +2 -0
- data/lib/aws-sdk-s3/plugins/s3_signer.rb +55 -92
- data/lib/aws-sdk-s3/plugins/skip_whole_multipart_get_checksums.rb +31 -0
- data/lib/aws-sdk-s3/plugins/sse_cpk.rb +3 -1
- data/lib/aws-sdk-s3/plugins/streaming_retry.rb +139 -0
- data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +2 -0
- data/lib/aws-sdk-s3/presigned_post.rb +108 -56
- data/lib/aws-sdk-s3/presigner.rb +169 -77
- data/lib/aws-sdk-s3/resource.rb +45 -5
- data/lib/aws-sdk-s3/types.rb +8564 -3891
- data/lib/aws-sdk-s3/waiters.rb +67 -1
- data/lib/aws-sdk-s3.rb +16 -6
- metadata +37 -13
data/lib/aws-sdk-s3/errors.rb
CHANGED
@@ -1,14 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# WARNING ABOUT GENERATED CODE
|
2
4
|
#
|
3
5
|
# This file is generated. See the contributing guide for more information:
|
4
|
-
# https://github.com/aws/aws-sdk-ruby/blob/
|
6
|
+
# https://github.com/aws/aws-sdk-ruby/blob/version-3/CONTRIBUTING.md
|
5
7
|
#
|
6
8
|
# WARNING ABOUT GENERATED CODE
|
7
9
|
|
8
10
|
module Aws::S3
|
11
|
+
|
12
|
+
# When S3 returns an error response, the Ruby SDK constructs and raises an error.
|
13
|
+
# These errors all extend Aws::S3::Errors::ServiceError < {Aws::Errors::ServiceError}
|
14
|
+
#
|
15
|
+
# You can rescue all S3 errors using ServiceError:
|
16
|
+
#
|
17
|
+
# begin
|
18
|
+
# # do stuff
|
19
|
+
# rescue Aws::S3::Errors::ServiceError
|
20
|
+
# # rescues all S3 API errors
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
#
|
24
|
+
# ## Request Context
|
25
|
+
# ServiceError objects have a {Aws::Errors::ServiceError#context #context} method that returns
|
26
|
+
# information about the request that generated the error.
|
27
|
+
# See {Seahorse::Client::RequestContext} for more information.
|
28
|
+
#
|
29
|
+
# ## Error Classes
|
30
|
+
# * {BucketAlreadyExists}
|
31
|
+
# * {BucketAlreadyOwnedByYou}
|
32
|
+
# * {InvalidObjectState}
|
33
|
+
# * {NoSuchBucket}
|
34
|
+
# * {NoSuchKey}
|
35
|
+
# * {NoSuchUpload}
|
36
|
+
# * {ObjectAlreadyInActiveTierError}
|
37
|
+
# * {ObjectNotInActiveTierError}
|
38
|
+
#
|
39
|
+
# Additionally, error classes are dynamically generated for service errors based on the error code
|
40
|
+
# if they are not defined above.
|
9
41
|
module Errors
|
10
42
|
|
11
43
|
extend Aws::Errors::DynamicErrors
|
12
44
|
|
45
|
+
class BucketAlreadyExists < ServiceError
|
46
|
+
|
47
|
+
# @param [Seahorse::Client::RequestContext] context
|
48
|
+
# @param [String] message
|
49
|
+
# @param [Aws::S3::Types::BucketAlreadyExists] data
|
50
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
51
|
+
super(context, message, data)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class BucketAlreadyOwnedByYou < ServiceError
|
56
|
+
|
57
|
+
# @param [Seahorse::Client::RequestContext] context
|
58
|
+
# @param [String] message
|
59
|
+
# @param [Aws::S3::Types::BucketAlreadyOwnedByYou] data
|
60
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
61
|
+
super(context, message, data)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class InvalidObjectState < ServiceError
|
66
|
+
|
67
|
+
# @param [Seahorse::Client::RequestContext] context
|
68
|
+
# @param [String] message
|
69
|
+
# @param [Aws::S3::Types::InvalidObjectState] data
|
70
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
71
|
+
super(context, message, data)
|
72
|
+
end
|
73
|
+
|
74
|
+
# @return [String]
|
75
|
+
def storage_class
|
76
|
+
@data[:storage_class]
|
77
|
+
end
|
78
|
+
|
79
|
+
# @return [String]
|
80
|
+
def access_tier
|
81
|
+
@data[:access_tier]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class NoSuchBucket < ServiceError
|
86
|
+
|
87
|
+
# @param [Seahorse::Client::RequestContext] context
|
88
|
+
# @param [String] message
|
89
|
+
# @param [Aws::S3::Types::NoSuchBucket] data
|
90
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
91
|
+
super(context, message, data)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class NoSuchKey < ServiceError
|
96
|
+
|
97
|
+
# @param [Seahorse::Client::RequestContext] context
|
98
|
+
# @param [String] message
|
99
|
+
# @param [Aws::S3::Types::NoSuchKey] data
|
100
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
101
|
+
super(context, message, data)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class NoSuchUpload < ServiceError
|
106
|
+
|
107
|
+
# @param [Seahorse::Client::RequestContext] context
|
108
|
+
# @param [String] message
|
109
|
+
# @param [Aws::S3::Types::NoSuchUpload] data
|
110
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
111
|
+
super(context, message, data)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class ObjectAlreadyInActiveTierError < ServiceError
|
116
|
+
|
117
|
+
# @param [Seahorse::Client::RequestContext] context
|
118
|
+
# @param [String] message
|
119
|
+
# @param [Aws::S3::Types::ObjectAlreadyInActiveTierError] data
|
120
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
121
|
+
super(context, message, data)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class ObjectNotInActiveTierError < ServiceError
|
126
|
+
|
127
|
+
# @param [Seahorse::Client::RequestContext] context
|
128
|
+
# @param [String] message
|
129
|
+
# @param [Aws::S3::Types::ObjectNotInActiveTierError] data
|
130
|
+
def initialize(context, message, data = Aws::EmptyStructure.new)
|
131
|
+
super(context, message, data)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
13
135
|
end
|
14
136
|
end
|
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# WARNING ABOUT GENERATED CODE
|
2
4
|
#
|
3
5
|
# This file is generated. See the contributing guide for more information:
|
4
|
-
# https://github.com/aws/aws-sdk-ruby/blob/
|
6
|
+
# https://github.com/aws/aws-sdk-ruby/blob/version-3/CONTRIBUTING.md
|
5
7
|
#
|
6
8
|
# WARNING ABOUT GENERATED CODE
|
7
9
|
|
@@ -14,27 +16,35 @@ module Aws::S3
|
|
14
16
|
end
|
15
17
|
|
16
18
|
def on_records_event(&block)
|
17
|
-
@event_emitter.on(:records,
|
19
|
+
@event_emitter.on(:records, block) if block_given?
|
18
20
|
end
|
19
21
|
|
20
22
|
def on_stats_event(&block)
|
21
|
-
@event_emitter.on(:stats,
|
23
|
+
@event_emitter.on(:stats, block) if block_given?
|
22
24
|
end
|
23
25
|
|
24
26
|
def on_progress_event(&block)
|
25
|
-
@event_emitter.on(:progress,
|
27
|
+
@event_emitter.on(:progress, block) if block_given?
|
26
28
|
end
|
27
29
|
|
28
30
|
def on_cont_event(&block)
|
29
|
-
@event_emitter.on(:cont,
|
31
|
+
@event_emitter.on(:cont, block) if block_given?
|
30
32
|
end
|
31
33
|
|
32
34
|
def on_end_event(&block)
|
33
|
-
@event_emitter.on(:end,
|
35
|
+
@event_emitter.on(:end, block) if block_given?
|
34
36
|
end
|
35
37
|
|
36
38
|
def on_error_event(&block)
|
37
|
-
@event_emitter.on(:error,
|
39
|
+
@event_emitter.on(:error, block) if block_given?
|
40
|
+
end
|
41
|
+
|
42
|
+
def on_initial_response_event(&block)
|
43
|
+
@event_emitter.on(:initial_response, block) if block_given?
|
44
|
+
end
|
45
|
+
|
46
|
+
def on_unknown_event(&block)
|
47
|
+
@event_emitter.on(:unknown_event, block) if block_given?
|
38
48
|
end
|
39
49
|
|
40
50
|
def on_event(&block)
|
@@ -43,6 +53,9 @@ module Aws::S3
|
|
43
53
|
on_progress_event(&block)
|
44
54
|
on_cont_event(&block)
|
45
55
|
on_end_event(&block)
|
56
|
+
on_error_event(&block)
|
57
|
+
on_initial_response_event(&block)
|
58
|
+
on_unknown_event(&block)
|
46
59
|
end
|
47
60
|
|
48
61
|
# @api private
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'pathname'
|
2
4
|
require 'thread'
|
3
5
|
require 'set'
|
@@ -21,7 +23,7 @@ module Aws
|
|
21
23
|
|
22
24
|
def download(destination, options = {})
|
23
25
|
@path = destination
|
24
|
-
@mode = options[:mode] ||
|
26
|
+
@mode = options[:mode] || 'auto'
|
25
27
|
@thread_count = options[:thread_count] || THREAD_COUNT
|
26
28
|
@chunk_size = options[:chunk_size]
|
27
29
|
@params = {
|
@@ -31,19 +33,19 @@ module Aws
|
|
31
33
|
@params[:version_id] = options[:version_id] if options[:version_id]
|
32
34
|
|
33
35
|
case @mode
|
34
|
-
when
|
35
|
-
when
|
36
|
-
when
|
36
|
+
when 'auto' then multipart_download
|
37
|
+
when 'single_request' then single_request
|
38
|
+
when 'get_range'
|
37
39
|
if @chunk_size
|
38
40
|
resp = @client.head_object(@params)
|
39
41
|
multithreaded_get_by_ranges(construct_chunks(resp.content_length))
|
40
42
|
else
|
41
|
-
msg =
|
43
|
+
msg = 'In :get_range mode, :chunk_size must be provided'
|
42
44
|
raise ArgumentError, msg
|
43
45
|
end
|
44
46
|
else
|
45
47
|
msg = "Invalid mode #{@mode} provided, "\
|
46
|
-
|
48
|
+
'mode should be :single_request, :get_range or :auto'
|
47
49
|
raise ArgumentError, msg
|
48
50
|
end
|
49
51
|
end
|
@@ -92,7 +94,12 @@ module Aws
|
|
92
94
|
if @chunk_size && @chunk_size > file_size
|
93
95
|
raise ArgumentError, ":chunk_size shouldn't exceed total file size."
|
94
96
|
else
|
95
|
-
@chunk_size || [
|
97
|
+
chunk_size = @chunk_size || [
|
98
|
+
(file_size.to_f / MAX_PARTS).ceil,
|
99
|
+
MIN_CHUNK_SIZE
|
100
|
+
].max.to_i
|
101
|
+
chunk_size -= 1 if file_size % chunk_size == 1
|
102
|
+
chunk_size
|
96
103
|
end
|
97
104
|
end
|
98
105
|
|
@@ -125,9 +132,9 @@ module Aws
|
|
125
132
|
end
|
126
133
|
|
127
134
|
def write(resp)
|
128
|
-
range, _ = resp.content_range.split(
|
129
|
-
head, _ = range.split(
|
130
|
-
|
135
|
+
range, _ = resp.content_range.split(' ').last.split('/')
|
136
|
+
head, _ = range.split('-').map {|s| s.to_i}
|
137
|
+
File.write(@path, resp.body.read, head)
|
131
138
|
end
|
132
139
|
|
133
140
|
def single_request
|
data/lib/aws-sdk-s3/file_part.rb
CHANGED
@@ -1,15 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Aws
|
2
4
|
module S3
|
3
5
|
|
4
|
-
# A utility class that provides an IO-like interface to a portion of
|
5
|
-
#
|
6
|
+
# A utility class that provides an IO-like interface to a portion of a file
|
7
|
+
# on disk.
|
6
8
|
# @api private
|
7
9
|
class FilePart
|
8
10
|
|
9
|
-
# @option options [required,String,Pathname,File,Tempfile] :source
|
10
|
-
#
|
11
|
+
# @option options [required, String, Pathname, File, Tempfile] :source
|
12
|
+
# The file to upload.
|
13
|
+
#
|
14
|
+
# @option options [required, Integer] :offset The file part will read
|
11
15
|
# starting at this byte offset.
|
12
|
-
#
|
16
|
+
#
|
17
|
+
# @option options [required, Integer] :size The maximum number of bytes to
|
13
18
|
# read from the `:offset`.
|
14
19
|
def initialize(options = {})
|
15
20
|
@source = options[:source]
|
@@ -19,7 +24,7 @@ module Aws
|
|
19
24
|
@file = nil
|
20
25
|
end
|
21
26
|
|
22
|
-
# @return [String,Pathname,File,Tempfile]
|
27
|
+
# @return [String, Pathname, File, Tempfile]
|
23
28
|
attr_reader :source
|
24
29
|
|
25
30
|
# @return [Integer]
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'pathname'
|
2
4
|
|
3
5
|
module Aws
|
@@ -5,44 +7,47 @@ module Aws
|
|
5
7
|
# @api private
|
6
8
|
class FileUploader
|
7
9
|
|
8
|
-
|
10
|
+
ONE_HUNDRED_MEGABYTES = 100 * 1024 * 1024
|
9
11
|
|
12
|
+
# @param [Hash] options
|
10
13
|
# @option options [Client] :client
|
11
|
-
# @option options [Integer] :multipart_threshold
|
12
|
-
# `:multipart_threshold` bytes are uploaded using S3 multipart APIs.
|
14
|
+
# @option options [Integer] :multipart_threshold (104857600)
|
13
15
|
def initialize(options = {})
|
14
16
|
@options = options
|
15
17
|
@client = options[:client] || Client.new
|
16
|
-
@multipart_threshold = options[:multipart_threshold] ||
|
18
|
+
@multipart_threshold = options[:multipart_threshold] ||
|
19
|
+
ONE_HUNDRED_MEGABYTES
|
17
20
|
end
|
18
21
|
|
19
22
|
# @return [Client]
|
20
23
|
attr_reader :client
|
21
24
|
|
22
|
-
# @return [Integer] Files larger than this in bytes are uploaded
|
25
|
+
# @return [Integer] Files larger than or equal to this in bytes are uploaded
|
23
26
|
# using a {MultipartFileUploader}.
|
24
27
|
attr_reader :multipart_threshold
|
25
28
|
|
26
|
-
# @param [String,Pathname,File,Tempfile] source
|
27
|
-
# @option options [required,String] :bucket
|
28
|
-
# @option options [required,String] :key
|
29
|
+
# @param [String, Pathname, File, Tempfile] source The file to upload.
|
30
|
+
# @option options [required, String] :bucket The bucket to upload to.
|
31
|
+
# @option options [required, String] :key The key for the object.
|
32
|
+
# @option options [Proc] :progress_callback
|
33
|
+
# A Proc that will be called when each chunk of the upload is sent.
|
34
|
+
# It will be invoked with [bytes_read], [total_sizes]
|
35
|
+
# @option options [Integer] :thread_count
|
36
|
+
# The thread count to use for multipart uploads. Ignored for
|
37
|
+
# objects smaller than the multipart threshold.
|
29
38
|
# @return [void]
|
30
39
|
def upload(source, options = {})
|
31
40
|
if File.size(source) >= multipart_threshold
|
32
41
|
MultipartFileUploader.new(@options).upload(source, options)
|
33
42
|
else
|
43
|
+
# remove multipart parameters not supported by put_object
|
44
|
+
options.delete(:thread_count)
|
34
45
|
put_object(source, options)
|
35
46
|
end
|
36
47
|
end
|
37
48
|
|
38
49
|
private
|
39
50
|
|
40
|
-
def put_object(source, options)
|
41
|
-
open_file(source) do |file|
|
42
|
-
@client.put_object(options.merge(body: file))
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
51
|
def open_file(source)
|
47
52
|
if String === source || Pathname === source
|
48
53
|
File.open(source, 'rb') { |file| yield(file) }
|
@@ -51,6 +56,20 @@ module Aws
|
|
51
56
|
end
|
52
57
|
end
|
53
58
|
|
59
|
+
def put_object(source, options)
|
60
|
+
if (callback = options.delete(:progress_callback))
|
61
|
+
options[:on_chunk_sent] = single_part_progress(callback)
|
62
|
+
end
|
63
|
+
open_file(source) do |file|
|
64
|
+
@client.put_object(options.merge(body: file))
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def single_part_progress(progress_callback)
|
69
|
+
proc do |_chunk, bytes_read, total_size|
|
70
|
+
progress_callback.call([bytes_read], [total_size])
|
71
|
+
end
|
72
|
+
end
|
54
73
|
end
|
55
74
|
end
|
56
75
|
end
|
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'set'
|
2
4
|
require 'time'
|
3
5
|
require 'openssl'
|
4
6
|
require 'cgi'
|
5
|
-
require 'webrick/httputils'
|
6
7
|
require 'aws-sdk-core/query'
|
7
8
|
|
8
9
|
module Aws
|
@@ -155,33 +156,24 @@ module Aws
|
|
155
156
|
end
|
156
157
|
|
157
158
|
def uri_escape(s)
|
158
|
-
|
159
159
|
#URI.escape(s)
|
160
160
|
|
161
|
-
#
|
162
|
-
#
|
163
|
-
#
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
167
|
-
#
|
168
|
-
# (
|
169
|
-
#
|
170
|
-
#
|
171
|
-
#
|
172
|
-
#
|
173
|
-
#
|
174
|
-
#
|
175
|
-
|
176
|
-
# URI.escape(s),
|
177
|
-
# ]
|
178
|
-
# next if e.uniq.length == 1
|
179
|
-
# puts("%5s %5s %5s %5s %5s %5s %5s" % ([s.inspect] + e))
|
180
|
-
# }
|
181
|
-
#
|
182
|
-
WEBrick::HTTPUtils.escape(s).gsub('%5B', '[').gsub('%5D', ']')
|
161
|
+
# (0..255).each {|c|
|
162
|
+
# s = [c].pack("C")
|
163
|
+
# e = [
|
164
|
+
# CGI.escape(s),
|
165
|
+
# ERB::Util.url_encode(s),
|
166
|
+
# URI.encode_www_form_component(s),
|
167
|
+
# WEBrick::HTTPUtils.escape_form(s),
|
168
|
+
# WEBrick::HTTPUtils.escape(s),
|
169
|
+
# URI.escape(s),
|
170
|
+
# URI::DEFAULT_PARSER.escape(s)
|
171
|
+
# ]
|
172
|
+
# next if e.uniq.length == 1
|
173
|
+
# puts("%5s %5s %5s %5s %5s %5s %5s %5s" % ([s.inspect] + e))
|
174
|
+
# }
|
175
|
+
URI::DEFAULT_PARSER.escape(s)
|
183
176
|
end
|
184
|
-
|
185
177
|
end
|
186
178
|
end
|
187
179
|
end
|
@@ -1,5 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'pathname'
|
2
|
-
require 'thread'
|
3
4
|
require 'set'
|
4
5
|
|
5
6
|
module Aws
|
@@ -16,14 +17,21 @@ module Aws
|
|
16
17
|
THREAD_COUNT = 10
|
17
18
|
|
18
19
|
# @api private
|
19
|
-
CREATE_OPTIONS =
|
20
|
-
|
20
|
+
CREATE_OPTIONS = Set.new(
|
21
|
+
Client.api.operation(:create_multipart_upload).input.shape.member_names
|
22
|
+
)
|
23
|
+
|
24
|
+
COMPLETE_OPTIONS = Set.new(
|
25
|
+
Client.api.operation(:complete_multipart_upload).input.shape.member_names
|
26
|
+
)
|
21
27
|
|
22
28
|
# @api private
|
23
|
-
UPLOAD_PART_OPTIONS =
|
24
|
-
|
29
|
+
UPLOAD_PART_OPTIONS = Set.new(
|
30
|
+
Client.api.operation(:upload_part).input.shape.member_names
|
31
|
+
)
|
25
32
|
|
26
33
|
# @option options [Client] :client
|
34
|
+
# @option options [Integer] :thread_count (THREAD_COUNT)
|
27
35
|
def initialize(options = {})
|
28
36
|
@client = options[:client] || Client.new
|
29
37
|
@thread_count = options[:thread_count] || THREAD_COUNT
|
@@ -32,10 +40,13 @@ module Aws
|
|
32
40
|
# @return [Client]
|
33
41
|
attr_reader :client
|
34
42
|
|
35
|
-
# @param [String,Pathname,File,Tempfile] source
|
36
|
-
# @option options [required,String] :bucket
|
37
|
-
# @option options [required,String] :key
|
38
|
-
# @
|
43
|
+
# @param [String, Pathname, File, Tempfile] source The file to upload.
|
44
|
+
# @option options [required, String] :bucket The bucket to upload to.
|
45
|
+
# @option options [required, String] :key The key for the object.
|
46
|
+
# @option options [Proc] :progress_callback
|
47
|
+
# A Proc that will be called when each chunk of the upload is sent.
|
48
|
+
# It will be invoked with [bytes_read], [total_sizes]
|
49
|
+
# @return [Seahorse::Client::Response] - the CompleteMultipartUploadResponse
|
39
50
|
def upload(source, options = {})
|
40
51
|
if File.size(source) < MIN_PART_SIZE
|
41
52
|
raise ArgumentError, FILE_TOO_SMALL
|
@@ -54,16 +65,17 @@ module Aws
|
|
54
65
|
|
55
66
|
def complete_upload(upload_id, parts, options)
|
56
67
|
@client.complete_multipart_upload(
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
68
|
+
**complete_opts(options).merge(
|
69
|
+
upload_id: upload_id,
|
70
|
+
multipart_upload: { parts: parts }
|
71
|
+
)
|
72
|
+
)
|
61
73
|
end
|
62
74
|
|
63
75
|
def upload_parts(upload_id, source, options)
|
64
76
|
pending = PartList.new(compute_parts(upload_id, source, options))
|
65
77
|
completed = PartList.new
|
66
|
-
errors = upload_in_threads(pending, completed)
|
78
|
+
errors = upload_in_threads(pending, completed, options)
|
67
79
|
if errors.empty?
|
68
80
|
completed.to_a.sort_by { |part| part[:part_number] }
|
69
81
|
else
|
@@ -93,7 +105,7 @@ module Aws
|
|
93
105
|
part_number = 1
|
94
106
|
parts = []
|
95
107
|
while offset < size
|
96
|
-
parts << upload_part_opts(options).merge(
|
108
|
+
parts << upload_part_opts(options).merge(
|
97
109
|
upload_id: upload_id,
|
98
110
|
part_number: part_number,
|
99
111
|
body: FilePart.new(
|
@@ -101,7 +113,7 @@ module Aws
|
|
101
113
|
offset: offset,
|
102
114
|
size: part_size(size, default_part_size, offset)
|
103
115
|
)
|
104
|
-
|
116
|
+
)
|
105
117
|
part_number += 1
|
106
118
|
offset += default_part_size
|
107
119
|
end
|
@@ -115,6 +127,13 @@ module Aws
|
|
115
127
|
end
|
116
128
|
end
|
117
129
|
|
130
|
+
def complete_opts(options)
|
131
|
+
COMPLETE_OPTIONS.inject({}) do |hash, key|
|
132
|
+
hash[key] = options[key] if options.key?(key)
|
133
|
+
hash
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
118
137
|
def upload_part_opts(options)
|
119
138
|
UPLOAD_PART_OPTIONS.inject({}) do |hash, key|
|
120
139
|
hash[key] = options[key] if options.key?(key)
|
@@ -122,15 +141,32 @@ module Aws
|
|
122
141
|
end
|
123
142
|
end
|
124
143
|
|
125
|
-
def upload_in_threads(pending, completed)
|
144
|
+
def upload_in_threads(pending, completed, options)
|
126
145
|
threads = []
|
146
|
+
if (callback = options[:progress_callback])
|
147
|
+
progress = MultipartProgress.new(pending, callback)
|
148
|
+
end
|
127
149
|
@thread_count.times do
|
128
150
|
thread = Thread.new do
|
129
151
|
begin
|
130
152
|
while part = pending.shift
|
153
|
+
if progress
|
154
|
+
part[:on_chunk_sent] =
|
155
|
+
proc do |_chunk, bytes, _total|
|
156
|
+
progress.call(part[:part_number], bytes)
|
157
|
+
end
|
158
|
+
end
|
131
159
|
resp = @client.upload_part(part)
|
132
160
|
part[:body].close
|
133
|
-
|
161
|
+
completed_part = {etag: resp.etag, part_number: part[:part_number]}
|
162
|
+
|
163
|
+
# get the requested checksum from the response
|
164
|
+
if part[:checksum_algorithm]
|
165
|
+
k = "checksum_#{part[:checksum_algorithm].downcase}".to_sym
|
166
|
+
completed_part[k] = resp[k]
|
167
|
+
end
|
168
|
+
|
169
|
+
completed.push(completed_part)
|
134
170
|
end
|
135
171
|
nil
|
136
172
|
rescue => error
|
@@ -177,11 +213,34 @@ module Aws
|
|
177
213
|
@mutex.synchronize { @parts.clear }
|
178
214
|
end
|
179
215
|
|
216
|
+
def size
|
217
|
+
@mutex.synchronize { @parts.size }
|
218
|
+
end
|
219
|
+
|
220
|
+
def part_sizes
|
221
|
+
@mutex.synchronize { @parts.map { |p| p[:body].size } }
|
222
|
+
end
|
223
|
+
|
180
224
|
def to_a
|
181
225
|
@mutex.synchronize { @parts.dup }
|
182
226
|
end
|
183
227
|
|
184
228
|
end
|
229
|
+
|
230
|
+
# @api private
|
231
|
+
class MultipartProgress
|
232
|
+
def initialize(parts, progress_callback)
|
233
|
+
@bytes_sent = Array.new(parts.size, 0)
|
234
|
+
@total_sizes = parts.part_sizes
|
235
|
+
@progress_callback = progress_callback
|
236
|
+
end
|
237
|
+
|
238
|
+
def call(part_number, bytes_read)
|
239
|
+
# part numbers start at 1
|
240
|
+
@bytes_sent[part_number - 1] = bytes_read
|
241
|
+
@progress_callback.call(@bytes_sent, @total_sizes)
|
242
|
+
end
|
243
|
+
end
|
185
244
|
end
|
186
245
|
end
|
187
|
-
end
|
246
|
+
end
|