aws-sdk 1.0.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.
- data/.yardopts +6 -0
- data/LICENSE.txt +171 -0
- data/NOTICE.txt +2 -0
- data/README.rdoc +189 -0
- data/lib/aws-sdk.rb +14 -0
- data/lib/aws.rb +63 -0
- data/lib/aws/api_config.rb +45 -0
- data/lib/aws/api_config/.document +0 -0
- data/lib/aws/api_config/EC2-2011-02-28.yml +2314 -0
- data/lib/aws/api_config/SNS-2010-03-31.yml +171 -0
- data/lib/aws/api_config/SQS-2009-02-01.yml +161 -0
- data/lib/aws/api_config/SimpleDB-2009-04-15.yml +278 -0
- data/lib/aws/api_config/SimpleEmailService-2010-12-01.yml +147 -0
- data/lib/aws/api_config_transform.rb +32 -0
- data/lib/aws/async_handle.rb +90 -0
- data/lib/aws/authorize_v2.rb +37 -0
- data/lib/aws/authorize_v3.rb +37 -0
- data/lib/aws/base_client.rb +524 -0
- data/lib/aws/cacheable.rb +92 -0
- data/lib/aws/common.rb +228 -0
- data/lib/aws/configurable.rb +36 -0
- data/lib/aws/configuration.rb +272 -0
- data/lib/aws/configured_client_methods.rb +81 -0
- data/lib/aws/configured_grammars.rb +65 -0
- data/lib/aws/configured_option_grammars.rb +46 -0
- data/lib/aws/configured_xml_grammars.rb +47 -0
- data/lib/aws/default_signer.rb +38 -0
- data/lib/aws/ec2.rb +321 -0
- data/lib/aws/ec2/attachment.rb +149 -0
- data/lib/aws/ec2/attachment_collection.rb +57 -0
- data/lib/aws/ec2/availability_zone.rb +80 -0
- data/lib/aws/ec2/availability_zone_collection.rb +47 -0
- data/lib/aws/ec2/block_device_mappings.rb +53 -0
- data/lib/aws/ec2/client.rb +54 -0
- data/lib/aws/ec2/client/xml.rb +127 -0
- data/lib/aws/ec2/collection.rb +39 -0
- data/lib/aws/ec2/config_transform.rb +63 -0
- data/lib/aws/ec2/elastic_ip.rb +107 -0
- data/lib/aws/ec2/elastic_ip_collection.rb +85 -0
- data/lib/aws/ec2/errors.rb +29 -0
- data/lib/aws/ec2/filtered_collection.rb +65 -0
- data/lib/aws/ec2/has_permissions.rb +46 -0
- data/lib/aws/ec2/image.rb +245 -0
- data/lib/aws/ec2/image_collection.rb +235 -0
- data/lib/aws/ec2/instance.rb +515 -0
- data/lib/aws/ec2/instance_collection.rb +276 -0
- data/lib/aws/ec2/key_pair.rb +86 -0
- data/lib/aws/ec2/key_pair_collection.rb +102 -0
- data/lib/aws/ec2/permission_collection.rb +177 -0
- data/lib/aws/ec2/region.rb +81 -0
- data/lib/aws/ec2/region_collection.rb +55 -0
- data/lib/aws/ec2/request.rb +27 -0
- data/lib/aws/ec2/reserved_instances.rb +50 -0
- data/lib/aws/ec2/reserved_instances_collection.rb +44 -0
- data/lib/aws/ec2/reserved_instances_offering.rb +55 -0
- data/lib/aws/ec2/reserved_instances_offering_collection.rb +43 -0
- data/lib/aws/ec2/resource.rb +340 -0
- data/lib/aws/ec2/resource_tag_collection.rb +218 -0
- data/lib/aws/ec2/security_group.rb +246 -0
- data/lib/aws/ec2/security_group/ip_permission.rb +70 -0
- data/lib/aws/ec2/security_group/ip_permission_collection.rb +59 -0
- data/lib/aws/ec2/security_group_collection.rb +132 -0
- data/lib/aws/ec2/snapshot.rb +138 -0
- data/lib/aws/ec2/snapshot_collection.rb +90 -0
- data/lib/aws/ec2/tag.rb +88 -0
- data/lib/aws/ec2/tag_collection.rb +114 -0
- data/lib/aws/ec2/tagged_collection.rb +48 -0
- data/lib/aws/ec2/tagged_item.rb +87 -0
- data/lib/aws/ec2/volume.rb +190 -0
- data/lib/aws/ec2/volume_collection.rb +95 -0
- data/lib/aws/errors.rb +129 -0
- data/lib/aws/http/builtin_handler.rb +69 -0
- data/lib/aws/http/curb_handler.rb +123 -0
- data/lib/aws/http/handler.rb +77 -0
- data/lib/aws/http/httparty_handler.rb +61 -0
- data/lib/aws/http/request.rb +136 -0
- data/lib/aws/http/request_param.rb +63 -0
- data/lib/aws/http/response.rb +75 -0
- data/lib/aws/ignore_result_element.rb +38 -0
- data/lib/aws/indifferent_hash.rb +86 -0
- data/lib/aws/inflection.rb +46 -0
- data/lib/aws/lazy_error_classes.rb +64 -0
- data/lib/aws/meta_utils.rb +43 -0
- data/lib/aws/model.rb +57 -0
- data/lib/aws/naming.rb +32 -0
- data/lib/aws/option_grammar.rb +544 -0
- data/lib/aws/policy.rb +912 -0
- data/lib/aws/rails.rb +209 -0
- data/lib/aws/record.rb +79 -0
- data/lib/aws/record/attribute.rb +94 -0
- data/lib/aws/record/attribute_macros.rb +288 -0
- data/lib/aws/record/attributes/boolean.rb +49 -0
- data/lib/aws/record/attributes/datetime.rb +86 -0
- data/lib/aws/record/attributes/float.rb +48 -0
- data/lib/aws/record/attributes/integer.rb +68 -0
- data/lib/aws/record/attributes/sortable_float.rb +60 -0
- data/lib/aws/record/attributes/sortable_integer.rb +95 -0
- data/lib/aws/record/attributes/string.rb +69 -0
- data/lib/aws/record/base.rb +728 -0
- data/lib/aws/record/conversion.rb +38 -0
- data/lib/aws/record/dirty_tracking.rb +286 -0
- data/lib/aws/record/errors.rb +153 -0
- data/lib/aws/record/exceptions.rb +48 -0
- data/lib/aws/record/finder_methods.rb +262 -0
- data/lib/aws/record/naming.rb +31 -0
- data/lib/aws/record/scope.rb +157 -0
- data/lib/aws/record/validations.rb +653 -0
- data/lib/aws/record/validator.rb +237 -0
- data/lib/aws/record/validators/acceptance.rb +51 -0
- data/lib/aws/record/validators/block.rb +38 -0
- data/lib/aws/record/validators/confirmation.rb +43 -0
- data/lib/aws/record/validators/count.rb +108 -0
- data/lib/aws/record/validators/exclusion.rb +43 -0
- data/lib/aws/record/validators/format.rb +57 -0
- data/lib/aws/record/validators/inclusion.rb +56 -0
- data/lib/aws/record/validators/length.rb +107 -0
- data/lib/aws/record/validators/numericality.rb +138 -0
- data/lib/aws/record/validators/presence.rb +45 -0
- data/lib/aws/resource_cache.rb +39 -0
- data/lib/aws/response.rb +113 -0
- data/lib/aws/response_cache.rb +50 -0
- data/lib/aws/s3.rb +109 -0
- data/lib/aws/s3/access_control_list.rb +252 -0
- data/lib/aws/s3/acl_object.rb +266 -0
- data/lib/aws/s3/bucket.rb +320 -0
- data/lib/aws/s3/bucket_collection.rb +122 -0
- data/lib/aws/s3/bucket_version_collection.rb +85 -0
- data/lib/aws/s3/client.rb +999 -0
- data/lib/aws/s3/client/xml.rb +190 -0
- data/lib/aws/s3/data_options.rb +99 -0
- data/lib/aws/s3/errors.rb +43 -0
- data/lib/aws/s3/multipart_upload.rb +318 -0
- data/lib/aws/s3/multipart_upload_collection.rb +78 -0
- data/lib/aws/s3/object_collection.rb +159 -0
- data/lib/aws/s3/object_metadata.rb +67 -0
- data/lib/aws/s3/object_upload_collection.rb +83 -0
- data/lib/aws/s3/object_version.rb +141 -0
- data/lib/aws/s3/object_version_collection.rb +78 -0
- data/lib/aws/s3/paginated_collection.rb +94 -0
- data/lib/aws/s3/policy.rb +76 -0
- data/lib/aws/s3/prefix_and_delimiter_collection.rb +56 -0
- data/lib/aws/s3/prefixed_collection.rb +84 -0
- data/lib/aws/s3/presigned_post.rb +504 -0
- data/lib/aws/s3/request.rb +198 -0
- data/lib/aws/s3/s3_object.rb +794 -0
- data/lib/aws/s3/tree.rb +116 -0
- data/lib/aws/s3/tree/branch_node.rb +71 -0
- data/lib/aws/s3/tree/child_collection.rb +108 -0
- data/lib/aws/s3/tree/leaf_node.rb +99 -0
- data/lib/aws/s3/tree/node.rb +22 -0
- data/lib/aws/s3/tree/parent.rb +90 -0
- data/lib/aws/s3/uploaded_part.rb +82 -0
- data/lib/aws/s3/uploaded_part_collection.rb +86 -0
- data/lib/aws/service_interface.rb +60 -0
- data/lib/aws/simple_db.rb +202 -0
- data/lib/aws/simple_db/attribute.rb +159 -0
- data/lib/aws/simple_db/attribute_collection.rb +227 -0
- data/lib/aws/simple_db/client.rb +52 -0
- data/lib/aws/simple_db/client/options.rb +34 -0
- data/lib/aws/simple_db/client/xml.rb +68 -0
- data/lib/aws/simple_db/consistent_read_option.rb +42 -0
- data/lib/aws/simple_db/delete_attributes.rb +64 -0
- data/lib/aws/simple_db/domain.rb +118 -0
- data/lib/aws/simple_db/domain_collection.rb +116 -0
- data/lib/aws/simple_db/domain_metadata.rb +112 -0
- data/lib/aws/simple_db/errors.rb +46 -0
- data/lib/aws/simple_db/expect_condition_option.rb +45 -0
- data/lib/aws/simple_db/item.rb +84 -0
- data/lib/aws/simple_db/item_collection.rb +594 -0
- data/lib/aws/simple_db/item_data.rb +70 -0
- data/lib/aws/simple_db/put_attributes.rb +62 -0
- data/lib/aws/simple_db/request.rb +27 -0
- data/lib/aws/simple_email_service.rb +373 -0
- data/lib/aws/simple_email_service/client.rb +39 -0
- data/lib/aws/simple_email_service/client/options.rb +24 -0
- data/lib/aws/simple_email_service/client/xml.rb +38 -0
- data/lib/aws/simple_email_service/email_address_collection.rb +66 -0
- data/lib/aws/simple_email_service/errors.rb +29 -0
- data/lib/aws/simple_email_service/quotas.rb +64 -0
- data/lib/aws/simple_email_service/request.rb +27 -0
- data/lib/aws/sns.rb +69 -0
- data/lib/aws/sns/client.rb +37 -0
- data/lib/aws/sns/client/options.rb +24 -0
- data/lib/aws/sns/client/xml.rb +38 -0
- data/lib/aws/sns/errors.rb +29 -0
- data/lib/aws/sns/policy.rb +49 -0
- data/lib/aws/sns/request.rb +27 -0
- data/lib/aws/sns/subscription.rb +100 -0
- data/lib/aws/sns/subscription_collection.rb +84 -0
- data/lib/aws/sns/topic.rb +384 -0
- data/lib/aws/sns/topic_collection.rb +70 -0
- data/lib/aws/sns/topic_subscription_collection.rb +58 -0
- data/lib/aws/sqs.rb +70 -0
- data/lib/aws/sqs/client.rb +38 -0
- data/lib/aws/sqs/client/xml.rb +36 -0
- data/lib/aws/sqs/errors.rb +33 -0
- data/lib/aws/sqs/policy.rb +50 -0
- data/lib/aws/sqs/queue.rb +507 -0
- data/lib/aws/sqs/queue_collection.rb +105 -0
- data/lib/aws/sqs/received_message.rb +184 -0
- data/lib/aws/sqs/received_sns_message.rb +112 -0
- data/lib/aws/sqs/request.rb +44 -0
- data/lib/aws/xml_grammar.rb +923 -0
- data/rails/init.rb +15 -0
- metadata +298 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Copyright 2011 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
|
4
|
+
# may not use this file except in compliance with the License. A copy of
|
|
5
|
+
# the License is located at
|
|
6
|
+
#
|
|
7
|
+
# http://aws.amazon.com/apache2.0/
|
|
8
|
+
#
|
|
9
|
+
# or in the "license" file accompanying this file. This file is
|
|
10
|
+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
|
11
|
+
# ANY KIND, either express or implied. See the License for the specific
|
|
12
|
+
# language governing permissions and limitations under the License.
|
|
13
|
+
|
|
14
|
+
require 'aws/http/request'
|
|
15
|
+
require 'aws/base_client'
|
|
16
|
+
require 'uri'
|
|
17
|
+
require 'time'
|
|
18
|
+
|
|
19
|
+
module AWS
|
|
20
|
+
class S3
|
|
21
|
+
|
|
22
|
+
# @private
|
|
23
|
+
class Request < AWS::Http::Request
|
|
24
|
+
|
|
25
|
+
# @param [bucket] S3 bucket name
|
|
26
|
+
attr_accessor :bucket
|
|
27
|
+
|
|
28
|
+
# @param [String] S3 object key
|
|
29
|
+
attr_accessor :key
|
|
30
|
+
|
|
31
|
+
attr_accessor :body_stream
|
|
32
|
+
|
|
33
|
+
def metadata= metadata
|
|
34
|
+
Array(metadata).each do |name, value|
|
|
35
|
+
headers["x-amz-meta-#{name}"] = value
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def canned_acl= acl
|
|
40
|
+
if acl.kind_of?(Symbol)
|
|
41
|
+
headers["x-amz-acl"] = acl.to_s.gsub("_", "-")
|
|
42
|
+
elsif acl
|
|
43
|
+
headers["x-amz-acl"] = acl
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def storage_class= storage_class
|
|
48
|
+
if storage_class.kind_of?(Symbol)
|
|
49
|
+
headers["x-amz-storage-class"] = storage_class.to_s.upcase
|
|
50
|
+
elsif storage_class
|
|
51
|
+
headers["x-amz-storage-class"] = storage_class
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def host
|
|
56
|
+
Client.dns_compatible_bucket_name?(bucket) ?
|
|
57
|
+
"#{bucket}.#{@host}" :
|
|
58
|
+
@host
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def path
|
|
62
|
+
parts = []
|
|
63
|
+
unless bucket.nil? or Client.dns_compatible_bucket_name?(bucket)
|
|
64
|
+
parts << bucket
|
|
65
|
+
end
|
|
66
|
+
parts << key if key
|
|
67
|
+
"/#{parts.join('/')}"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def querystring
|
|
71
|
+
url_encoded_params
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @param [String, IO] The http request body. This can be a string or
|
|
75
|
+
# any object that responds to #read and #eof? (like an IO object).
|
|
76
|
+
def body= body
|
|
77
|
+
@body_stream = StringIO.new(body)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @return [String, nil] The http request body.
|
|
81
|
+
def body
|
|
82
|
+
if @body_stream
|
|
83
|
+
string = @body_stream.read
|
|
84
|
+
@body_stream.rewind
|
|
85
|
+
string
|
|
86
|
+
else
|
|
87
|
+
nil
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# From the S3 developer guide:
|
|
92
|
+
#
|
|
93
|
+
# StringToSign =
|
|
94
|
+
# HTTP-Verb + "\n" +
|
|
95
|
+
# content-md5 + "\n" +
|
|
96
|
+
# content-type + "\n" +
|
|
97
|
+
# date + "\n" +
|
|
98
|
+
# CanonicalizedAmzHeaders + CanonicalizedResource;
|
|
99
|
+
#
|
|
100
|
+
def string_to_sign
|
|
101
|
+
[
|
|
102
|
+
http_method,
|
|
103
|
+
headers.values_at('content-md5', 'content-type').join("\n"),
|
|
104
|
+
signing_string_date,
|
|
105
|
+
canonicalized_headers,
|
|
106
|
+
canonicalized_resource,
|
|
107
|
+
].flatten.compact.join("\n")
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def signing_string_date
|
|
111
|
+
# if a date is provided via x-amz-date then we should omit the
|
|
112
|
+
# Date header from the signing string (should appear as a blank line)
|
|
113
|
+
if headers.detect{|k,v| k.to_s =~ /^x-amz-date$/i }
|
|
114
|
+
''
|
|
115
|
+
else
|
|
116
|
+
headers['date'] ||= Time.now.rfc822
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# From the S3 developer guide
|
|
121
|
+
#
|
|
122
|
+
# CanonicalizedResource =
|
|
123
|
+
# [ "/" + Bucket ] +
|
|
124
|
+
# <HTTP-Request-URI, from the protocol name up to the querystring> +
|
|
125
|
+
# [ sub-resource, if present. e.g. "?acl", "?location",
|
|
126
|
+
# "?logging", or "?torrent"];
|
|
127
|
+
#
|
|
128
|
+
def canonicalized_resource
|
|
129
|
+
|
|
130
|
+
parts = []
|
|
131
|
+
|
|
132
|
+
# virtual hosted-style requests require the hostname to appear
|
|
133
|
+
# in the canonicalized resource prefixed by a forward slash.
|
|
134
|
+
if Client.dns_compatible_bucket_name?(bucket)
|
|
135
|
+
parts << "/#{bucket}"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# all requests require the portion of the un-decoded uri up to
|
|
139
|
+
# but not including the query string
|
|
140
|
+
parts << path
|
|
141
|
+
|
|
142
|
+
# lastly any sub resource querystring params need to be appened
|
|
143
|
+
# in lexigraphical ordered joined by '&' and prefixed by '?'
|
|
144
|
+
params = (sub_resource_params +
|
|
145
|
+
query_parameters_for_signature)
|
|
146
|
+
unless params.empty?
|
|
147
|
+
parts << '?'
|
|
148
|
+
parts << params.sort.collect{|p| p.to_s }.join('&')
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
parts.join
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# CanonicalizedAmzHeaders
|
|
155
|
+
#
|
|
156
|
+
# See the developer guide for more information on how this element
|
|
157
|
+
# is generated.
|
|
158
|
+
#
|
|
159
|
+
def canonicalized_headers
|
|
160
|
+
x_amz = headers.select{|name, value| name.to_s =~ /^x-amz-/i }
|
|
161
|
+
x_amz = x_amz.collect{|name, value| [name.downcase, value] }
|
|
162
|
+
x_amz = x_amz.sort_by{|name, value| name }
|
|
163
|
+
x_amz = x_amz.collect{|name, value| "#{name}:#{value}" }.join("\n")
|
|
164
|
+
x_amz == '' ? nil : x_amz
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def sub_resource_params
|
|
168
|
+
params.select{|p| self.class.sub_resources.include?(p.name) }
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def query_parameters_for_signature
|
|
172
|
+
params.select { |p| self.class.query_parameters.include?(p.name) }
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def add_authorization!(signer)
|
|
176
|
+
signature = URI.escape(signer.sign(string_to_sign, 'sha1'))
|
|
177
|
+
headers["authorization"] = "AWS #{signer.access_key_id}:#{signature}"
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
class << self
|
|
181
|
+
|
|
182
|
+
def sub_resources
|
|
183
|
+
%w(acl location logging notification partNumber policy
|
|
184
|
+
requestPayment torrent uploadId uploads versionId
|
|
185
|
+
versioning versions)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def query_parameters
|
|
189
|
+
%w(response-content-type response-content-language
|
|
190
|
+
response-expires response-cache-control
|
|
191
|
+
response-content-disposition response-content-encoding)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
@@ -0,0 +1,794 @@
|
|
|
1
|
+
# Copyright 2011 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
|
4
|
+
# may not use this file except in compliance with the License. A copy of
|
|
5
|
+
# the License is located at
|
|
6
|
+
#
|
|
7
|
+
# http://aws.amazon.com/apache2.0/
|
|
8
|
+
#
|
|
9
|
+
# or in the "license" file accompanying this file. This file is
|
|
10
|
+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
|
11
|
+
# ANY KIND, either express or implied. See the License for the specific
|
|
12
|
+
# language governing permissions and limitations under the License.
|
|
13
|
+
|
|
14
|
+
require 'aws/model'
|
|
15
|
+
require 'aws/s3/request'
|
|
16
|
+
require 'aws/s3/object_metadata'
|
|
17
|
+
require 'aws/s3/multipart_upload'
|
|
18
|
+
require 'aws/s3/object_upload_collection'
|
|
19
|
+
require 'aws/s3/presigned_post'
|
|
20
|
+
require 'aws/s3/data_options'
|
|
21
|
+
require 'uri'
|
|
22
|
+
|
|
23
|
+
module AWS
|
|
24
|
+
class S3
|
|
25
|
+
|
|
26
|
+
# Represents an object in S3 identified by a key.
|
|
27
|
+
#
|
|
28
|
+
# object = bucket.objects["key-to-my-object"]
|
|
29
|
+
# object.key #=> 'key-to-my-object'
|
|
30
|
+
#
|
|
31
|
+
# See {ObjectCollection} for more information on finding objects.
|
|
32
|
+
#
|
|
33
|
+
# == Writing and Reading S3Objects
|
|
34
|
+
#
|
|
35
|
+
# obj = bucket.objects["my-text-object"]
|
|
36
|
+
#
|
|
37
|
+
# obj.write("MY TEXT")
|
|
38
|
+
# obj.read
|
|
39
|
+
# #=> "MY TEXT"
|
|
40
|
+
#
|
|
41
|
+
# obj.write(File.new("README.txt"))
|
|
42
|
+
# obj.read
|
|
43
|
+
# # should equal File.read("README.txt")
|
|
44
|
+
#
|
|
45
|
+
class S3Object
|
|
46
|
+
|
|
47
|
+
include Model
|
|
48
|
+
include DataOptions
|
|
49
|
+
|
|
50
|
+
# @param [Bucket] bucket The bucket this object belongs to.
|
|
51
|
+
# @param [String] key The object's key.
|
|
52
|
+
def initialize(bucket, key, opts = {})
|
|
53
|
+
super
|
|
54
|
+
@key = key
|
|
55
|
+
@bucket = bucket
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @return [String] The objects unique key
|
|
59
|
+
attr_reader :key
|
|
60
|
+
|
|
61
|
+
# @return [Bucket] The bucket this object is in.
|
|
62
|
+
attr_reader :bucket
|
|
63
|
+
|
|
64
|
+
# @private
|
|
65
|
+
def inspect
|
|
66
|
+
"<#{self.class}:#{bucket.name}/#{key}>"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @return [Boolean] Returns true if the other object belongs to the
|
|
70
|
+
# same bucket and has the same key.
|
|
71
|
+
def ==(other)
|
|
72
|
+
other.kind_of?(S3Object) and other.bucket == bucket and other.key == key
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
alias_method :eql?, :==
|
|
76
|
+
|
|
77
|
+
# Performs a HEAD request against this object and returns an object
|
|
78
|
+
# with useful information about the object, including:
|
|
79
|
+
#
|
|
80
|
+
# * metadata (hash of user-supplied key-value pairs)
|
|
81
|
+
# * content_length (integer, number of bytes)
|
|
82
|
+
# * content_type (as sent to S3 when uploading the object)
|
|
83
|
+
# * etag (typically the object's MD5)
|
|
84
|
+
#
|
|
85
|
+
# @param [Hash] options
|
|
86
|
+
# @option options [String] :version_id Which version of this object
|
|
87
|
+
# to make a HEAD request against.
|
|
88
|
+
# @return [Response] A head object response with metatadata,
|
|
89
|
+
# content_length, content_type and etag.
|
|
90
|
+
def head options = {}
|
|
91
|
+
client.head_object(options.merge(
|
|
92
|
+
:bucket_name => bucket.name, :key => key))
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Returns the object's ETag.
|
|
96
|
+
#
|
|
97
|
+
# Generally the ETAG is the MD5 of the object. If the object was
|
|
98
|
+
# uploaded using multipart upload then this is the MD5 all of the
|
|
99
|
+
# upload-part-md5s.
|
|
100
|
+
#
|
|
101
|
+
# @return [String] Returns the object's ETag
|
|
102
|
+
def etag
|
|
103
|
+
head.etag
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# @return [Integer] Size of the object in bytes.
|
|
107
|
+
def content_length
|
|
108
|
+
head.content_length
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# @note S3 does not compute content-type. It reports the content-type
|
|
112
|
+
# as was reported during the file upload.
|
|
113
|
+
# @return [String] Returns the content type as reported by S3,
|
|
114
|
+
# defaults to an empty string when not provided during upload.
|
|
115
|
+
def content_type
|
|
116
|
+
head.content_type
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Deletes the object from its S3 bucket.
|
|
120
|
+
#
|
|
121
|
+
# @param [Hash] options
|
|
122
|
+
# @option [String] :version_id (nil) If present the specified version
|
|
123
|
+
# of this object will be deleted. Only works for buckets that have
|
|
124
|
+
# had versioning enabled.
|
|
125
|
+
# @return [Response]
|
|
126
|
+
def delete options = {}
|
|
127
|
+
options[:bucket_name] = bucket.name
|
|
128
|
+
options[:key] = key
|
|
129
|
+
client.delete_object(options)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# @option [String] :version_id (nil) If present the metadata object
|
|
133
|
+
# will be for the specified version.
|
|
134
|
+
# @return [ObjectMetadata] Returns an instance of ObjectMetadata
|
|
135
|
+
# representing the metadata for this object.
|
|
136
|
+
def metadata options = {}
|
|
137
|
+
options[:config] = config
|
|
138
|
+
ObjectMetadata.new(self, options)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Returns a colletion representing all the object versions
|
|
142
|
+
# for this object.
|
|
143
|
+
#
|
|
144
|
+
# bucket.versioning_enabled? # => true
|
|
145
|
+
# version = bucket.objects["mykey"].versions.latest
|
|
146
|
+
#
|
|
147
|
+
# @return [ObjectVersionCollection]
|
|
148
|
+
def versions
|
|
149
|
+
ObjectVersionCollection.new(self)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Writes data to the object in S3. This method will attempt
|
|
153
|
+
# to intelligently choose between uploading in one request and
|
|
154
|
+
# using {#multipart_upload}.
|
|
155
|
+
#
|
|
156
|
+
# Unless versioning is enabled, any data currently in S3 at {#key}
|
|
157
|
+
# will be replaced.
|
|
158
|
+
#
|
|
159
|
+
# You can pass +:data+ or +:file+ as the first argument or as
|
|
160
|
+
# options. Example usage:
|
|
161
|
+
#
|
|
162
|
+
# obj = s3.buckets.mybucket.objects.mykey
|
|
163
|
+
# obj.write("HELLO")
|
|
164
|
+
# obj.write(:data => "HELLO")
|
|
165
|
+
# obj.write(Pathname.new("myfile"))
|
|
166
|
+
# obj.write(:file => "myfile")
|
|
167
|
+
#
|
|
168
|
+
# # writes zero-length data
|
|
169
|
+
# obj.write(:metadata => { "avg-rating" => "5 stars" })
|
|
170
|
+
#
|
|
171
|
+
# @overload write(options = {})
|
|
172
|
+
# @overload write(data, options = {})
|
|
173
|
+
#
|
|
174
|
+
# @param data The data to upload (see the +:data+
|
|
175
|
+
# option).
|
|
176
|
+
#
|
|
177
|
+
# @param options [Hash] Additional upload options.
|
|
178
|
+
#
|
|
179
|
+
# @option options :data The data to upload. Valid values include:
|
|
180
|
+
# * A string
|
|
181
|
+
#
|
|
182
|
+
# * A Pathname object
|
|
183
|
+
#
|
|
184
|
+
# * Any object responding to +read+ and +eof?+; the object
|
|
185
|
+
# must support the following access methods:
|
|
186
|
+
# read # all at once
|
|
187
|
+
# read(length) until eof? # in chunks
|
|
188
|
+
#
|
|
189
|
+
# If you specify data this way, you must also include the
|
|
190
|
+
# +:content_length+ option.
|
|
191
|
+
#
|
|
192
|
+
# @option options [String] :file Can be specified instead of +:data+;
|
|
193
|
+
# its value specifies the path of a file to upload.
|
|
194
|
+
#
|
|
195
|
+
# @option options [Boolean] :single_request If this option is
|
|
196
|
+
# true, the method will always generate exactly one request
|
|
197
|
+
# to S3 regardless of how much data is being uploaded.
|
|
198
|
+
#
|
|
199
|
+
# @option options [Integer] :content_length If provided, this
|
|
200
|
+
# option must match the total number of bytes written to S3
|
|
201
|
+
# during the operation. This option is required if +:data+
|
|
202
|
+
# is an IO-like object without a +size+ method.
|
|
203
|
+
#
|
|
204
|
+
# @option options [Integer] :multipart_threshold Specifies the
|
|
205
|
+
# maximum size in bytes of a single-request upload. If the
|
|
206
|
+
# data being uploaded is larger than this threshold, it will
|
|
207
|
+
# be uploaded using {#multipart_upload}.
|
|
208
|
+
#
|
|
209
|
+
# @option options [Integer] :multipart_min_part_size The
|
|
210
|
+
# minimum size of a part if a multi-part upload is used. S3
|
|
211
|
+
# will reject non-final parts smaller than 5MB, and the
|
|
212
|
+
# default for this option is 5MB.
|
|
213
|
+
#
|
|
214
|
+
# @option options [Hash] :metadata A hash of metadata to be
|
|
215
|
+
# included with the object. These will be sent to S3 as
|
|
216
|
+
# headers prefixed with +x-amz-meta+.
|
|
217
|
+
#
|
|
218
|
+
# @option options [Symbol] :acl A canned access control
|
|
219
|
+
# policy. Valid values are:
|
|
220
|
+
# * +:private+
|
|
221
|
+
# * +:public_read+
|
|
222
|
+
# * +:public_read_write+
|
|
223
|
+
# * +:authenticated_read+
|
|
224
|
+
# * +:bucket_owner_read+
|
|
225
|
+
# * +:bucket_owner_full_control+
|
|
226
|
+
#
|
|
227
|
+
# @option options [Symbol] :storage_class Controls whether
|
|
228
|
+
# Reduced Redundancy Storage is enabled for the object.
|
|
229
|
+
# Valid values are +:standard+ (the default) or
|
|
230
|
+
# +:reduced_redundancy+.
|
|
231
|
+
#
|
|
232
|
+
# @option options :cache_control [String] Can be used to specify
|
|
233
|
+
# caching behavior. See
|
|
234
|
+
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
|
|
235
|
+
#
|
|
236
|
+
# @option options :content_disposition [String] Specifies
|
|
237
|
+
# presentational information for the object. See
|
|
238
|
+
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec19.5.1
|
|
239
|
+
#
|
|
240
|
+
# @option options :content_encoding [String] Specifies what
|
|
241
|
+
# content encodings have been applied to the object and thus
|
|
242
|
+
# what decoding mechanisms must be applied to obtain the
|
|
243
|
+
# media-type referenced by the +Content-Type+ header field.
|
|
244
|
+
# See
|
|
245
|
+
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
|
|
246
|
+
#
|
|
247
|
+
# @option options :content_type A standard MIME type
|
|
248
|
+
# describing the format of the object data.
|
|
249
|
+
#
|
|
250
|
+
# @return [S3Object, ObjectVersion] If the bucket has versioning
|
|
251
|
+
# enabled, returns the {ObjectVersion} representing the
|
|
252
|
+
# version that was uploaded. If versioning is disabled,
|
|
253
|
+
# returns self.
|
|
254
|
+
def write(options_or_data = nil, options = nil)
|
|
255
|
+
|
|
256
|
+
(data_options, put_options) =
|
|
257
|
+
compute_put_options(options_or_data, options)
|
|
258
|
+
|
|
259
|
+
if use_multipart?(data_options, put_options)
|
|
260
|
+
put_options.delete(:multipart_threshold)
|
|
261
|
+
multipart_upload(put_options) do |upload|
|
|
262
|
+
each_part(data_options, put_options) do |part|
|
|
263
|
+
upload.add_part(part)
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
else
|
|
267
|
+
opts = { :bucket_name => bucket.name, :key => key }
|
|
268
|
+
resp = client.put_object(opts.merge(put_options).merge(data_options))
|
|
269
|
+
if resp.version_id
|
|
270
|
+
ObjectVersion.new(self, resp.version_id)
|
|
271
|
+
else
|
|
272
|
+
self
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Performs a multipart upload. Use this if you have specific
|
|
278
|
+
# needs for how the upload is split into parts, or if you want
|
|
279
|
+
# to have more control over how the failure of an individual
|
|
280
|
+
# part upload is handled. Otherwise, {#write} is much simpler
|
|
281
|
+
# to use.
|
|
282
|
+
#
|
|
283
|
+
# @example Uploading an object in two parts
|
|
284
|
+
# bucket.objects.myobject.multipart_upload do |upload|
|
|
285
|
+
# upload.add_part("a" * 5242880)
|
|
286
|
+
# upload.add_part("b" * 2097152)
|
|
287
|
+
# end
|
|
288
|
+
#
|
|
289
|
+
# @example Uploading parts out of order
|
|
290
|
+
# bucket.objects.myobject.multipart_upload do |upload|
|
|
291
|
+
# upload.add_part("b" * 2097152, :part_number => 2)
|
|
292
|
+
# upload.add_part("a" * 5242880, :part_number => 1)
|
|
293
|
+
# end
|
|
294
|
+
#
|
|
295
|
+
# @example Aborting an upload after parts have been added
|
|
296
|
+
# bucket.objects.myobject.multipart_upload do |upload|
|
|
297
|
+
# upload.add_part("b" * 2097152, :part_number => 2)
|
|
298
|
+
# upload.abort
|
|
299
|
+
# end
|
|
300
|
+
#
|
|
301
|
+
# @example Starting an upload and completing it later by ID
|
|
302
|
+
# upload = bucket.objects.myobject.multipart_upload
|
|
303
|
+
# upload.add_part("a" * 5242880)
|
|
304
|
+
# upload.add_part("b" * 2097152)
|
|
305
|
+
# id = upload.id
|
|
306
|
+
#
|
|
307
|
+
# # later or in a different process
|
|
308
|
+
# upload = bucket.objects.myobject.multipart_uploads[id]
|
|
309
|
+
# upload.complete(:remote_parts)
|
|
310
|
+
#
|
|
311
|
+
# @yieldparam [MultipartUpload] upload A handle to the upload.
|
|
312
|
+
# {MultipartUpload#close} is called in an +ensure+ clause so
|
|
313
|
+
# that the upload will always be either completed or
|
|
314
|
+
# aborted.
|
|
315
|
+
#
|
|
316
|
+
# @param [Hash] options Options for the upload.
|
|
317
|
+
#
|
|
318
|
+
# @option options [Hash] :metadata A hash of metadata to be
|
|
319
|
+
# included with the object. These will be sent to S3 as
|
|
320
|
+
# headers prefixed with +x-amz-meta+.
|
|
321
|
+
#
|
|
322
|
+
# @option options [Symbol] :acl A canned access control
|
|
323
|
+
# policy. Valid values are:
|
|
324
|
+
# * +:private+
|
|
325
|
+
# * +:public_read+
|
|
326
|
+
# * +:public_read_write+
|
|
327
|
+
# * +:authenticated_read+
|
|
328
|
+
# * +:bucket_owner_read+
|
|
329
|
+
# * +:bucket_owner_full_control+
|
|
330
|
+
#
|
|
331
|
+
# @option options [Boolean] :reduced_redundancy If true,
|
|
332
|
+
# Reduced Redundancy Storage will be enabled for the
|
|
333
|
+
# uploaded object.
|
|
334
|
+
#
|
|
335
|
+
# @option options :cache_control [String] Can be used to specify
|
|
336
|
+
# caching behavior. See
|
|
337
|
+
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
|
|
338
|
+
#
|
|
339
|
+
# @option options :content_disposition [String] Specifies
|
|
340
|
+
# presentational information for the object. See
|
|
341
|
+
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec19.5.1
|
|
342
|
+
#
|
|
343
|
+
# @option options :content_encoding [String] Specifies what
|
|
344
|
+
# content encodings have been applied to the object and thus
|
|
345
|
+
# what decoding mechanisms must be applied to obtain the
|
|
346
|
+
# media-type referenced by the +Content-Type+ header field.
|
|
347
|
+
# See
|
|
348
|
+
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
|
|
349
|
+
#
|
|
350
|
+
# @option options :content_type A standard MIME type
|
|
351
|
+
# describing the format of the object data.
|
|
352
|
+
#
|
|
353
|
+
# @return [S3Object, ObjectVersion] If the bucket has versioning
|
|
354
|
+
# enabled, returns the {ObjectVersion} representing the
|
|
355
|
+
# version that was uploaded. If versioning is disabled,
|
|
356
|
+
# returns self.
|
|
357
|
+
def multipart_upload(options = {})
|
|
358
|
+
upload = multipart_uploads.create(options)
|
|
359
|
+
|
|
360
|
+
if block_given?
|
|
361
|
+
result = nil
|
|
362
|
+
begin
|
|
363
|
+
yield(upload)
|
|
364
|
+
ensure
|
|
365
|
+
result = upload.close
|
|
366
|
+
end
|
|
367
|
+
result
|
|
368
|
+
else
|
|
369
|
+
upload
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# @example Abort any in-progress uploads for the object:
|
|
374
|
+
#
|
|
375
|
+
# object.multipart_uploads.each(&:abort)
|
|
376
|
+
#
|
|
377
|
+
# @return [ObjectUploadCollection] Returns an object representing the
|
|
378
|
+
# collection of uploads that are in progress for this object.
|
|
379
|
+
def multipart_uploads
|
|
380
|
+
ObjectUploadCollection.new(self)
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
# Copies data from one S3 object to another.
|
|
384
|
+
#
|
|
385
|
+
# S3 handles the copy so the clients does not need to fetch the data
|
|
386
|
+
# and upload it again. You can also change the storage class and
|
|
387
|
+
# metadata of the object when copying.
|
|
388
|
+
#
|
|
389
|
+
# @param [Mixed] source
|
|
390
|
+
# @param [Hash] options
|
|
391
|
+
# @option options [String] :bucket_name The name of the bucket
|
|
392
|
+
# the source object can be found in. Defaults to the current
|
|
393
|
+
# object's bucket.
|
|
394
|
+
# @option options [Bucket] :bucket The bucket the source object can
|
|
395
|
+
# be found in. Defaults to the current object's bucket.
|
|
396
|
+
# @option options [Hash] :metadata A hash of metadata to save with
|
|
397
|
+
# the copied object. When blank, the sources metadata is copied.
|
|
398
|
+
# @option options [Boolean] :reduced_redundancy (false) If true the
|
|
399
|
+
# object is stored with reduced redundancy in S3 for a lower cost.
|
|
400
|
+
# @option options [String] :version_id (nil) Causes the copy to
|
|
401
|
+
# read a specific version of the source object.
|
|
402
|
+
# @return [nil]
|
|
403
|
+
def copy_from source, options = {}
|
|
404
|
+
|
|
405
|
+
copy_opts = { :bucket_name => bucket.name, :key => key }
|
|
406
|
+
|
|
407
|
+
copy_opts[:copy_source] = case source
|
|
408
|
+
when S3Object
|
|
409
|
+
"#{source.bucket.name}/#{source.key}"
|
|
410
|
+
when ObjectVersion
|
|
411
|
+
copy_opts[:version_id] = source.version_id
|
|
412
|
+
"#{source.object.bucket.name}/#{source.object.key}"
|
|
413
|
+
else
|
|
414
|
+
case
|
|
415
|
+
when options[:bucket] then "#{options[:bucket].name}/#{source}"
|
|
416
|
+
when options[:bucket_name] then "#{options[:bucket_name]}/#{source}"
|
|
417
|
+
else "#{self.bucket.name}/#{source}"
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
if options[:metadata]
|
|
422
|
+
copy_opts[:metadata] = options[:metadata]
|
|
423
|
+
copy_opts[:metadata_directive] = 'REPLACE'
|
|
424
|
+
else
|
|
425
|
+
copy_opts[:metadata_directive] = 'COPY'
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
copy_opts[:version_id] = options[:version_id] if options[:version_id]
|
|
429
|
+
|
|
430
|
+
copy_opts[:storage_class] = 'REDUCED_REDUNDANCY' if
|
|
431
|
+
options[:reduced_redundancy]
|
|
432
|
+
|
|
433
|
+
client.copy_object(copy_opts)
|
|
434
|
+
|
|
435
|
+
nil
|
|
436
|
+
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
# Copies data from the current object to another object in S3.
|
|
440
|
+
#
|
|
441
|
+
# S3 handles the copy so the client does not need to fetch the data
|
|
442
|
+
# and upload it again. You can also change the storage class and
|
|
443
|
+
# metadata of the object when copying.
|
|
444
|
+
#
|
|
445
|
+
# @param [S3Object,String] target An S3Object, or a string key of
|
|
446
|
+
# and object to copy to.
|
|
447
|
+
# @param [Hash] options
|
|
448
|
+
# @option options [String] :bucket_name The name of the bucket
|
|
449
|
+
# the object should be copied into. Defaults to the current object's
|
|
450
|
+
# bucket.
|
|
451
|
+
# @option options [Bucket] :bucket The bucket the target object
|
|
452
|
+
# should be copied into. Defaults to the current object's bucket.
|
|
453
|
+
# @option options [Hash] :metadata A hash of metadata to save with
|
|
454
|
+
# the copied object. When blank, the sources metadata is copied.
|
|
455
|
+
# @option options [Boolean] :reduced_redundancy (false) If true the
|
|
456
|
+
# object is stored with reduced redundancy in S3 for a lower cost.
|
|
457
|
+
# @return (see #copy_from)
|
|
458
|
+
def copy_to target, options = {}
|
|
459
|
+
|
|
460
|
+
unless target.is_a?(S3Object)
|
|
461
|
+
|
|
462
|
+
bucket = case
|
|
463
|
+
when options[:bucket] then options[:bucket]
|
|
464
|
+
when options[:bucket_name]
|
|
465
|
+
Bucket.new(options[:bucket_name], :config => config)
|
|
466
|
+
else self.bucket
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
target = S3Object.new(bucket, target)
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
copy_opts = options.dup
|
|
473
|
+
copy_opts.delete(:bucket)
|
|
474
|
+
copy_opts.delete(:bucket_name)
|
|
475
|
+
|
|
476
|
+
target.copy_from(self, copy_opts)
|
|
477
|
+
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
# Fetches the object data from S3.
|
|
481
|
+
#
|
|
482
|
+
# @example Reading data as a string
|
|
483
|
+
# object.write('some data')
|
|
484
|
+
# object.read
|
|
485
|
+
# #=> 'some data'
|
|
486
|
+
#
|
|
487
|
+
# @param [Hash] options
|
|
488
|
+
# @option options [String] :version_id Reads data from a
|
|
489
|
+
# specific version of this object.
|
|
490
|
+
# @option options [Time] :if_unmodified_since Causes #read
|
|
491
|
+
# to return nil if the object was modified since the
|
|
492
|
+
# given time.
|
|
493
|
+
# @option options [Time] :if_modified_since Causes #read
|
|
494
|
+
# to return nil unless the object was modified since the
|
|
495
|
+
# given time.
|
|
496
|
+
# @option options [String] :if_match If specified, the method
|
|
497
|
+
# will return nil (and not fetch any data) unless the object ETag
|
|
498
|
+
# @option options [Range] :range A byte range to read data from
|
|
499
|
+
def read(options = {}, &blk)
|
|
500
|
+
options[:bucket_name] = bucket.name
|
|
501
|
+
options[:key] = key
|
|
502
|
+
client.get_object(options).data
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
# @private
|
|
506
|
+
module ACLProxy
|
|
507
|
+
|
|
508
|
+
attr_accessor :object
|
|
509
|
+
|
|
510
|
+
def change
|
|
511
|
+
yield(self)
|
|
512
|
+
object.acl = self
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
# Returns the object's access control list. This will be an
|
|
518
|
+
# instance of AccessControlList, plus an additional +change+
|
|
519
|
+
# method:
|
|
520
|
+
#
|
|
521
|
+
# object.acl.change do |acl|
|
|
522
|
+
# # remove any grants to someone other than the bucket owner
|
|
523
|
+
# owner_id = object.bucket.owner.id
|
|
524
|
+
# acl.grants.reject! do |g|
|
|
525
|
+
# g.grantee.canonical_user_id != owner_id
|
|
526
|
+
# end
|
|
527
|
+
# end
|
|
528
|
+
#
|
|
529
|
+
# Note that changing the ACL is not an atomic operation; it
|
|
530
|
+
# fetches the current ACL, yields it to the block, and then
|
|
531
|
+
# sets it again. Therefore, it's possible that you may
|
|
532
|
+
# overwrite a concurrent update to the ACL using this
|
|
533
|
+
# method.
|
|
534
|
+
#
|
|
535
|
+
# @return [AccessControlList]
|
|
536
|
+
#
|
|
537
|
+
def acl
|
|
538
|
+
acl = client.get_object_acl(
|
|
539
|
+
:bucket_name => bucket.name,
|
|
540
|
+
:key => key
|
|
541
|
+
).acl
|
|
542
|
+
acl.extend ACLProxy
|
|
543
|
+
acl.object = self
|
|
544
|
+
acl
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
# Sets the object's access control list. +acl+ can be:
|
|
548
|
+
# * An XML policy as a string (which is passed to S3 uninterpreted)
|
|
549
|
+
# * An AccessControlList object
|
|
550
|
+
# * Any object that responds to +to_xml+
|
|
551
|
+
# * Any Hash that is acceptable as an argument to
|
|
552
|
+
# AccessControlList#initialize.
|
|
553
|
+
#
|
|
554
|
+
# @param (see Bucket#acl=)
|
|
555
|
+
# @return [Response]
|
|
556
|
+
#
|
|
557
|
+
def acl=(acl)
|
|
558
|
+
client.set_object_acl(
|
|
559
|
+
:bucket_name => bucket.name,
|
|
560
|
+
:key => key,
|
|
561
|
+
:acl => acl)
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
# @private
|
|
565
|
+
REQUEST_PARAMETERS = Request.query_parameters.map do |p|
|
|
566
|
+
p.tr("-","_").to_sym
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
# Generates a presigned URL for an operation on this object.
|
|
570
|
+
# This URL can be used by a regular HTTP client to perform the
|
|
571
|
+
# desired operation without credentials and without changing
|
|
572
|
+
# the permissions of the object.
|
|
573
|
+
#
|
|
574
|
+
# @example Generate a url to read an object
|
|
575
|
+
# bucket.objects.myobject.url_for(:read)
|
|
576
|
+
#
|
|
577
|
+
# @example Generate a url to delete an object
|
|
578
|
+
# bucket.objects.myobject.url_for(:delete)
|
|
579
|
+
#
|
|
580
|
+
# @example Override response headers for reading an object
|
|
581
|
+
# object = bucket.objects.myobject
|
|
582
|
+
# url = object.url_for(:read, :response_content_type => "application/json")
|
|
583
|
+
#
|
|
584
|
+
# @example Generate a url that expires in 10 minutes
|
|
585
|
+
# bucket.objects.myobject.url_for(:read, :expires => 10*60)
|
|
586
|
+
#
|
|
587
|
+
# @param [Symbol, String] method The HTTP verb or object
|
|
588
|
+
# method for which the returned URL will be valid. Valid
|
|
589
|
+
# values:
|
|
590
|
+
#
|
|
591
|
+
# * +:get+ or +:read+
|
|
592
|
+
# * +:put+ or +:write+
|
|
593
|
+
# * +:delete+
|
|
594
|
+
#
|
|
595
|
+
# @param [Hash] options Additional options for generating the URL.
|
|
596
|
+
#
|
|
597
|
+
# @option options :expires Sets the expiration time of the
|
|
598
|
+
# URL; after this time S3 will return an error if the URL is
|
|
599
|
+
# used. This can be an integer (to specify the number of
|
|
600
|
+
# seconds after the current time), a string (which is parsed
|
|
601
|
+
# as a date using Time#parse), a Time, or a DateTime object.
|
|
602
|
+
# This option defaults to one hour after the current time.
|
|
603
|
+
#
|
|
604
|
+
# @option options [String] :secure Whether to generate a
|
|
605
|
+
# secure (HTTPS) URL or a plain HTTP url.
|
|
606
|
+
#
|
|
607
|
+
# @option options [String] :response_content_type Sets the
|
|
608
|
+
# Content-Type header of the response when performing an
|
|
609
|
+
# HTTP GET on the returned URL.
|
|
610
|
+
#
|
|
611
|
+
# @option options [String] :response_content_language Sets the
|
|
612
|
+
# Content-Language header of the response when performing an
|
|
613
|
+
# HTTP GET on the returned URL.
|
|
614
|
+
#
|
|
615
|
+
# @option options [String] :response_expires Sets the Expires
|
|
616
|
+
# header of the response when performing an HTTP GET on the
|
|
617
|
+
# returned URL.
|
|
618
|
+
#
|
|
619
|
+
# @option options [String] :response_cache_control Sets the
|
|
620
|
+
# Cache-Control header of the response when performing an
|
|
621
|
+
# HTTP GET on the returned URL.
|
|
622
|
+
#
|
|
623
|
+
# @option options [String] :response_content_disposition Sets
|
|
624
|
+
# the Content-Disposition header of the response when
|
|
625
|
+
# performing an HTTP GET on the returned URL.
|
|
626
|
+
#
|
|
627
|
+
# @option options [String] :response_content_encoding Sets the
|
|
628
|
+
# Content-Encoding header of the response when performing an
|
|
629
|
+
# HTTP GET on the returned URL.
|
|
630
|
+
# @return [URI::HTTP, URI::HTTPS]
|
|
631
|
+
def url_for(method, options = {})
|
|
632
|
+
req = request_for_signing(options)
|
|
633
|
+
|
|
634
|
+
method = http_method(method)
|
|
635
|
+
expires = expiration_timestamp(options[:expires])
|
|
636
|
+
req.add_param("AWSAccessKeyId", config.signer.access_key_id)
|
|
637
|
+
req.add_param("Signature", signature(method, expires, req))
|
|
638
|
+
req.add_param("Expires", expires)
|
|
639
|
+
|
|
640
|
+
build_uri(options[:secure] != false, req)
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
# Generates a public (not authenticated) URL for the object.
|
|
644
|
+
#
|
|
645
|
+
# @param [Hash] options Options for generating the URL.
|
|
646
|
+
#
|
|
647
|
+
# @option options [Boolean] :secure Whether to generate a
|
|
648
|
+
# secure (HTTPS) URL or a plain HTTP url.
|
|
649
|
+
# @return [URI::HTTP, URI::HTTPS]
|
|
650
|
+
def public_url(options = {})
|
|
651
|
+
req = request_for_signing(options)
|
|
652
|
+
build_uri(options[:secure] != false, req)
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
# Generates fields for a presigned POST to this object. This
|
|
656
|
+
# method adds a constraint that the key must match the key of
|
|
657
|
+
# this object. All options are sent to the PresignedPost
|
|
658
|
+
# constructor.
|
|
659
|
+
#
|
|
660
|
+
# @see PresignedPost
|
|
661
|
+
# @return [PresignedPost]
|
|
662
|
+
def presigned_post(options = {})
|
|
663
|
+
PresignedPost.new(bucket, options.merge(:key => key))
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
# @private
|
|
667
|
+
private
|
|
668
|
+
def build_uri(secure, request)
|
|
669
|
+
uri_class = secure ? URI::HTTPS : URI::HTTP
|
|
670
|
+
uri_class.build(:host => request.host,
|
|
671
|
+
:path => request.path,
|
|
672
|
+
:query => request.querystring)
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
# @private
|
|
676
|
+
private
|
|
677
|
+
def signature(method, expires, request)
|
|
678
|
+
string_to_sign = [method,
|
|
679
|
+
"", "",
|
|
680
|
+
expires,
|
|
681
|
+
request.canonicalized_resource].join("\n")
|
|
682
|
+
config.signer.sign(string_to_sign, "sha1")
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
# @private
|
|
686
|
+
private
|
|
687
|
+
def expiration_timestamp(input)
|
|
688
|
+
case input
|
|
689
|
+
when Time
|
|
690
|
+
expires = input.to_i
|
|
691
|
+
when DateTime
|
|
692
|
+
expires = Time.parse(input.to_s).to_i
|
|
693
|
+
when Integer
|
|
694
|
+
expires = (Time.now + input).to_i
|
|
695
|
+
when String
|
|
696
|
+
expires = Time.parse(input).to_i
|
|
697
|
+
else
|
|
698
|
+
expires = (Time.now + 60*60).to_i
|
|
699
|
+
end
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
# @private
|
|
703
|
+
private
|
|
704
|
+
def http_method(input)
|
|
705
|
+
symbol = case input
|
|
706
|
+
when :read then :get
|
|
707
|
+
when :write then :put
|
|
708
|
+
else
|
|
709
|
+
input
|
|
710
|
+
end
|
|
711
|
+
symbol.to_s.upcase
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
# @private
|
|
715
|
+
private
|
|
716
|
+
def request_for_signing(options)
|
|
717
|
+
req = Request.new
|
|
718
|
+
|
|
719
|
+
req.bucket = bucket.name
|
|
720
|
+
req.key = key
|
|
721
|
+
req.host = config.s3_endpoint
|
|
722
|
+
|
|
723
|
+
REQUEST_PARAMETERS.each do |param|
|
|
724
|
+
req.add_param(param.to_s.tr("_","-"),
|
|
725
|
+
options[param]) if options.key?(param)
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
req
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
# @private
|
|
732
|
+
private
|
|
733
|
+
def compute_put_options(options_or_data, options)
|
|
734
|
+
put_opts = {}
|
|
735
|
+
|
|
736
|
+
if options
|
|
737
|
+
|
|
738
|
+
raise ArgumentError.new("Object data passed twice (argument and file path)") if
|
|
739
|
+
options[:file]
|
|
740
|
+
|
|
741
|
+
raise ArgumentError.new("Object data passed twice (argument and option)") if
|
|
742
|
+
options[:data]
|
|
743
|
+
|
|
744
|
+
[{ :data => options_or_data }, options]
|
|
745
|
+
elsif options_or_data.kind_of?(Hash)
|
|
746
|
+
if file = options_or_data[:file]
|
|
747
|
+
data_options = { :file => file }
|
|
748
|
+
else
|
|
749
|
+
data_options = { :data => options_or_data[:data] || "" }
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
[data_options, options_or_data]
|
|
753
|
+
else
|
|
754
|
+
[{ :data => options_or_data || "" }, {}]
|
|
755
|
+
end
|
|
756
|
+
end
|
|
757
|
+
|
|
758
|
+
# @private
|
|
759
|
+
private
|
|
760
|
+
def use_multipart?(data_options, options)
|
|
761
|
+
threshold = options[:multipart_threshold] ||
|
|
762
|
+
config.s3_multipart_threshold
|
|
763
|
+
|
|
764
|
+
!options[:single_request] and
|
|
765
|
+
size = content_length_from(data_options.merge(options)) and
|
|
766
|
+
size > threshold
|
|
767
|
+
end
|
|
768
|
+
|
|
769
|
+
# @private
|
|
770
|
+
private
|
|
771
|
+
def each_part(data_options, options)
|
|
772
|
+
total_size = content_length_from(data_options.merge(options))
|
|
773
|
+
part_size = optimal_part_size(total_size, options)
|
|
774
|
+
stream = data_stream_from(data_options.merge(options))
|
|
775
|
+
while !stream.eof?
|
|
776
|
+
yield(stream.read(part_size))
|
|
777
|
+
end
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
# @private
|
|
781
|
+
private
|
|
782
|
+
def optimal_part_size(total_size, options)
|
|
783
|
+
maximum_parts = config.s3_multipart_max_parts
|
|
784
|
+
min_size = options[:multipart_min_part_size] ||
|
|
785
|
+
config.s3_multipart_min_part_size
|
|
786
|
+
|
|
787
|
+
[(total_size.to_f / maximum_parts.to_f).ceil,
|
|
788
|
+
min_size].max.to_i
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
end
|
|
792
|
+
|
|
793
|
+
end
|
|
794
|
+
end
|