s33r 0.2 → 0.3
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/bin/s3cli.rb +25 -16
- data/html/classes/MIME.html +120 -0
- data/html/classes/MIME/InvalidContentType.html +119 -0
- data/html/classes/MIME/Type.html +1173 -0
- data/html/classes/MIME/Types.html +566 -0
- data/html/classes/Net.html +108 -0
- data/html/classes/Net/HTTPGenericRequest.html +233 -0
- data/html/classes/Net/HTTPResponse.html +242 -0
- data/html/classes/S33r.html +743 -0
- data/html/classes/S33r/BucketListing.html +372 -0
- data/html/classes/S33r/Client.html +981 -0
- data/html/classes/S33r/NamedBucket.html +620 -0
- data/html/classes/S33r/S33rException.html +118 -0
- data/html/classes/S33r/S33rException/BucketListingMaxKeysError.html +111 -0
- data/html/classes/S33r/S33rException/InvalidBucketListing.html +111 -0
- data/html/classes/S33r/S33rException/MalformedBucketName.html +111 -0
- data/html/classes/S33r/S33rException/MethodNotAvailable.html +111 -0
- data/html/classes/S33r/S33rException/MissingRequiredHeaders.html +111 -0
- data/html/classes/S33r/S33rException/MissingResource.html +111 -0
- data/html/classes/S33r/S33rException/UnsupportedCannedACL.html +111 -0
- data/html/classes/S33r/S33rException/UnsupportedHTTPMethod.html +111 -0
- data/html/classes/S33r/S3Object.html +307 -0
- data/html/classes/S33r/S3User.html +171 -0
- data/html/classes/S33r/Sync.html +151 -0
- data/html/classes/XML.html +200 -0
- data/html/classes/XML/Document.html +125 -0
- data/html/classes/XML/Node.html +124 -0
- data/html/created.rid +1 -0
- data/html/files/CHANGELOG.html +101 -0
- data/html/files/MIT-LICENSE.html +129 -0
- data/html/files/README_txt.html +209 -0
- data/html/files/lib/s33r/bucket_listing_rb.html +116 -0
- data/html/files/lib/s33r/client_rb.html +110 -0
- data/html/files/lib/s33r/core_rb.html +113 -0
- data/html/files/lib/s33r/libxml_extensions_rb.html +107 -0
- data/html/files/lib/s33r/mimetypes_rb.html +120 -0
- data/html/files/lib/s33r/named_bucket_rb.html +101 -0
- data/html/files/lib/s33r/s33r_exception_rb.html +101 -0
- data/html/files/lib/s33r/s33r_http_rb.html +108 -0
- data/html/files/lib/s33r/sync_rb.html +101 -0
- data/html/files/lib/s33r_rb.html +101 -0
- data/html/fr_class_index.html +52 -0
- data/html/fr_file_index.html +39 -0
- data/html/fr_method_index.html +126 -0
- data/html/index.html +24 -0
- data/html/rdoc-style.css +208 -0
- data/lib/s33r/bucket_listing.rb +69 -60
- data/lib/s33r/client.rb +150 -73
- data/lib/s33r/core.rb +56 -44
- data/lib/s33r/libxml_extensions.rb +10 -5
- data/lib/s33r/mimetypes.rb +3 -2
- data/lib/s33r/named_bucket.rb +89 -24
- data/lib/s33r/{s3_exception.rb → s33r_exception.rb} +2 -2
- data/lib/s33r/{net_http_overrides.rb → s33r_http.rb} +29 -21
- data/lib/s33r/sync.rb +4 -2
- data/test/cases/spec_bucket_listing.rb +10 -13
- data/test/cases/spec_client.rb +65 -0
- data/test/cases/spec_core.rb +16 -11
- data/test/cases/spec_namedbucket.rb +32 -0
- data/test/cases/spec_sync.rb +6 -5
- data/test/cases/spec_xml.rb +1 -1
- data/test/files/client_config.yml +6 -0
- data/test/files/namedbucket_config.yml +12 -0
- data/test/{s3_test_constants.rb → test_setup.rb} +7 -6
- metadata +63 -11
- data/LICENSE.txt +0 -22
- data/MIT-LICENSE +0 -21
- data/README.txt +0 -19
- data/bin/config.yml +0 -5
- data/test/cases/unit_client.rb +0 -40
- data/test/cases/unit_named_bucket.rb +0 -12
data/lib/s33r/bucket_listing.rb
CHANGED
@@ -1,35 +1,49 @@
|
|
1
|
-
# use prefix to limit keys to some subset of all available keys;
|
2
|
-
# use delimiter to group keys
|
1
|
+
# TODO: use prefix to limit keys to some subset of all available keys;
|
2
|
+
# TODO: use delimiter to group keys
|
3
3
|
|
4
4
|
require 'date'
|
5
5
|
require 'xml/libxml'
|
6
6
|
|
7
|
-
module
|
7
|
+
module S33r
|
8
|
+
# Object representation of the content of a bucket.
|
8
9
|
class BucketListing
|
9
|
-
attr_reader :
|
10
|
-
|
11
|
-
#
|
12
|
-
|
13
|
-
#
|
14
|
-
attr_reader :
|
15
|
-
|
16
|
-
|
10
|
+
attr_reader :delimiter, :prefix, :marker, :max_keys, :is_truncated, :common_prefixes
|
11
|
+
|
12
|
+
# Name of the bucket this listing is for.
|
13
|
+
attr_reader :name
|
14
|
+
# Hash of objects in this bucket, keyed by their S3 keys.
|
15
|
+
attr_reader :contents
|
16
|
+
# A NamedBucket instance associated with this listing.
|
17
|
+
attr_accessor :named_bucket
|
18
|
+
|
19
|
+
# Create a new object representing a ListBucketResult.
|
20
|
+
#
|
21
|
+
# +bucket_listing_xml+ is a ListBucketResult document, as returned from a GET on a bucket
|
22
|
+
# (see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/).
|
23
|
+
#
|
24
|
+
# +named_bucket+ can be set to an existing NamedBucket instance, so that any objects
|
25
|
+
# inside this listing can be associated with that instance. This enables objects to be easily deleted
|
26
|
+
# without having to create a new Client instance.
|
27
|
+
def initialize(bucket_listing_xml, named_bucket=nil)
|
17
28
|
@contents = {}
|
18
29
|
@common_prefixes = {}
|
30
|
+
# the NamedBucket instance associated with this listing (if any)
|
31
|
+
@named_bucket = named_bucket
|
19
32
|
set_listing_xml(bucket_listing_xml)
|
20
33
|
end
|
21
34
|
|
35
|
+
# Convert a ListBucketResult XML document into an object representation.
|
22
36
|
def set_listing_xml(bucket_listing_xml)
|
23
37
|
# remove the namespace declaration: libxml doesn't like it
|
24
38
|
bucket_listing_xml.gsub!(/ xmlns="http:\/\/s3.amazonaws.com\/doc\/2006-03-01\/"/, '')
|
25
|
-
@listing_xml = bucket_listing_xml
|
26
39
|
parse_listing(bucket_listing_xml)
|
27
40
|
rescue
|
28
41
|
message = "Cannot create bucket listing from supplied XML"
|
29
42
|
message += " (was nil)" if bucket_listing_xml.nil?
|
30
|
-
raise
|
43
|
+
raise S33rException::InvalidBucketListing, message
|
31
44
|
end
|
32
45
|
|
46
|
+
# Parse raw XML ListBucketResponse from S3 into object instances.
|
33
47
|
def parse_listing(bucket_listing_xml)
|
34
48
|
doc = XML.get_xml_doc(bucket_listing_xml)
|
35
49
|
|
@@ -48,43 +62,30 @@ module S3
|
|
48
62
|
|
49
63
|
# contents
|
50
64
|
doc.find('//Contents').to_a.each do |node|
|
51
|
-
obj = S3Object.new
|
52
|
-
|
65
|
+
obj = S3Object.new(node)
|
66
|
+
# Add to the content listing for the bucket
|
53
67
|
@contents[obj.key] = obj
|
54
68
|
end
|
55
69
|
end
|
56
70
|
|
57
|
-
#
|
71
|
+
# Return an object in this bucket by key.
|
58
72
|
def [](key)
|
59
73
|
@contents[key]
|
60
74
|
end
|
75
|
+
|
76
|
+
# Pretty listing of keys in alphabetical order.
|
77
|
+
def pretty
|
78
|
+
@contents.keys.sort.each { |k| puts k }
|
79
|
+
end
|
61
80
|
|
62
|
-
#
|
81
|
+
# Setters which perform some type casts and normalisation.
|
63
82
|
private
|
64
|
-
def name=(val)
|
65
|
-
|
66
|
-
end
|
67
|
-
|
68
|
-
def
|
69
|
-
|
70
|
-
end
|
71
|
-
|
72
|
-
def delimiter=(val)
|
73
|
-
@delimiter = string_prop_normalise(val)
|
74
|
-
end
|
75
|
-
|
76
|
-
def marker=(val)
|
77
|
-
@marker = string_prop_normalise(val)
|
78
|
-
end
|
79
|
-
|
80
|
-
def max_keys=(val)
|
81
|
-
@max_keys = val.to_i
|
82
|
-
end
|
83
|
-
|
84
|
-
def is_truncated=(val)
|
85
|
-
@is_truncated = false
|
86
|
-
@is_truncated = true if ('true' == val || true == val || 'True' == val)
|
87
|
-
end
|
83
|
+
def name=(val); @name = string_prop_normalise(val); end
|
84
|
+
def prefix=(val); @prefix = string_prop_normalise(val); end
|
85
|
+
def delimiter=(val); @delimiter = string_prop_normalise(val); end
|
86
|
+
def marker=(val); @marker = string_prop_normalise(val); end
|
87
|
+
def max_keys=(val); @max_keys = val.to_i; end
|
88
|
+
def is_truncated=(val); @is_truncated = ('true' == val || true == val || 'True' == val); end
|
88
89
|
|
89
90
|
# normalise string properties:
|
90
91
|
# if value for XML element is nil, set property to empty string
|
@@ -95,44 +96,52 @@ module S3
|
|
95
96
|
|
96
97
|
end
|
97
98
|
|
99
|
+
# Representation of an object stored in a bucket.
|
98
100
|
class S3Object
|
99
|
-
attr_reader :
|
100
|
-
|
101
|
-
|
102
|
-
#
|
103
|
-
def initialize(named_bucket=nil)
|
101
|
+
attr_reader :named_bucket, :key, :last_modified, :etag, :size, :owner, :storage_class
|
102
|
+
attr_writer :named_bucket
|
103
|
+
|
104
|
+
# Create from a node.
|
105
|
+
def initialize(node=nil, named_bucket=nil)
|
104
106
|
@named_bucket = named_bucket
|
107
|
+
self.set_from_node(node) unless node.nil?
|
108
|
+
end
|
109
|
+
|
110
|
+
# Remove this object from associated NamedBucket.
|
111
|
+
def delete
|
112
|
+
@named_bucket.delete_key(@key) unless @named_bucket.nil?
|
105
113
|
end
|
106
114
|
|
107
|
-
#
|
108
|
-
#
|
115
|
+
# Set properties of the object from an XML string.
|
116
|
+
#
|
117
|
+
# +xml_str+ should be a string representing a full XML document,
|
118
|
+
# containing a <Contents> element as its root element.
|
109
119
|
def set_from_xml_string(xml_str)
|
110
|
-
|
111
|
-
set_from_node(doc)
|
120
|
+
set_from_node(XML.get_xml_doc(xml_str))
|
112
121
|
end
|
113
122
|
|
114
|
-
#
|
115
|
-
#
|
116
|
-
#
|
117
|
-
|
118
|
-
def set_from_node(doc, options={})
|
123
|
+
# Set properties of the object from an XML document.
|
124
|
+
#
|
125
|
+
# +doc+: XML::Document instance to parse to get properties for this object.
|
126
|
+
def set_from_node(doc)
|
119
127
|
@key = doc.xget('Key')
|
120
128
|
@last_modified = DateTime.parse(doc.xget('LastModified'))
|
121
129
|
@etag = doc.xget('ETag').gsub("\"", "")
|
122
130
|
@size = doc.xget('Size').to_i
|
123
131
|
@owner = S3User.new(doc.find('Owner').to_a.first)
|
124
132
|
|
125
|
-
if
|
126
|
-
|
127
|
-
|
128
|
-
|
133
|
+
# TODO: if setting from a full object listing (GET on a resource key),
|
134
|
+
# do additional field setting here (e.g. x-amz-meta- headers)
|
135
|
+
# and assign the response body to some data field; detect whether
|
136
|
+
# these fields exist before attempting to set properties
|
129
137
|
end
|
130
138
|
end
|
131
139
|
|
132
140
|
class S3User
|
133
141
|
attr_accessor :id, :display_name
|
134
142
|
|
135
|
-
# XML::Document
|
143
|
+
# +owner_xml_doc+: XML::Document instance, representing an <Owner> node from
|
144
|
+
# inside a ListBucketResult <Contents> element (see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/).
|
136
145
|
def initialize(owner_xml_doc)
|
137
146
|
@id = owner_xml_doc.xget('//ID')
|
138
147
|
@display_name = owner_xml_doc.xget('//DisplayName')
|
data/lib/s33r/client.rb
CHANGED
@@ -1,25 +1,36 @@
|
|
1
1
|
require 'net/https'
|
2
2
|
require 'cgi'
|
3
3
|
|
4
|
-
module
|
4
|
+
module S33r
|
5
5
|
include Net
|
6
6
|
|
7
|
-
#
|
7
|
+
# The client performs operations over the network,
|
8
8
|
# using the core to build request headers and content;
|
9
9
|
# only client-specific headers are managed here: other headers
|
10
|
-
# can be handled by the core
|
11
|
-
|
12
|
-
# TODO:
|
10
|
+
# can be handled by the core.
|
11
|
+
#--
|
12
|
+
# TODO: need to wrap XML returned into object representation.
|
13
|
+
# TODO: use customisable thread pool for requests.
|
14
|
+
#--
|
13
15
|
class Client
|
14
|
-
include
|
16
|
+
include S33r
|
15
17
|
|
16
|
-
attr_accessor :
|
17
|
-
|
18
|
-
#
|
19
|
-
|
20
|
-
|
18
|
+
attr_accessor :aws_access_key, :aws_secret_access_key
|
19
|
+
|
20
|
+
# Size of data chunk to be sent per request when putting data.
|
21
|
+
attr_accessor :chunk_size
|
22
|
+
|
23
|
+
# Headers which should be sent with every request by default (unless overridden).
|
24
|
+
attr_accessor :client_headers
|
25
|
+
|
26
|
+
# Configure either an SSL-enabled or plain HTTP client.
|
27
|
+
# (If using SSL, no verification of server certificate is performed.)
|
28
|
+
#
|
29
|
+
# +options+: hash of optional client config.:
|
30
|
+
# * <tt>:use_ssl => false</tt>: only use plain HTTP for connections
|
31
|
+
# * <tt>:dump_requests => true</tt>: dump each request's initial line and headers to STDOUT
|
21
32
|
def initialize(aws_access_key, aws_secret_access_key, options={})
|
22
|
-
if false == options[:
|
33
|
+
if false == options[:use_ssl]
|
23
34
|
@client = HTTP.new(HOST, NON_SSL_PORT)
|
24
35
|
@client.use_ssl = false
|
25
36
|
else
|
@@ -31,8 +42,8 @@ module S3
|
|
31
42
|
|
32
43
|
@dump_requests = (true == options[:dump_requests])
|
33
44
|
|
34
|
-
# set default chunk size for streaming request body
|
35
|
-
@chunk_size =
|
45
|
+
# set default chunk size for streaming request body
|
46
|
+
@chunk_size = DEFAULT_CHUNK_SIZE
|
36
47
|
|
37
48
|
# Amazon S3 developer keys
|
38
49
|
@aws_access_key = aws_access_key
|
@@ -40,16 +51,51 @@ module S3
|
|
40
51
|
|
41
52
|
# headers sent with every request made by this client
|
42
53
|
@client_headers = {}
|
54
|
+
end
|
55
|
+
|
56
|
+
# Initialise client from YAML configuration file
|
57
|
+
# (see load_config method for details of acceptable format).
|
58
|
+
def Client.init(config_file)
|
59
|
+
aws_access_key, aws_secret_access_key, options, _ = load_config(config_file)
|
60
|
+
Client.new(aws_access_key, aws_secret_access_key, options)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Load YAML config. file for a client. The config. file looks like this:
|
64
|
+
#
|
65
|
+
# :include: test/files/namedbucket_config.yml
|
66
|
+
#
|
67
|
+
# The +options+ section contains settings specific to Client and NamedClient instances; +custom+
|
68
|
+
# contains extra settings specific to your application.
|
69
|
+
# +options+ and +custom+ sections can be omitted, but settings for AWS keys are required.
|
70
|
+
#
|
71
|
+
# Returns an array <tt>[aws_access_key, aws_secret_access_key, options, custom]</tt>, where +options+
|
72
|
+
# and +custom+ are hashes.
|
73
|
+
def Client.load_config(config_file)
|
74
|
+
require 'yaml'
|
75
|
+
config = YAML::load_file(config_file)
|
76
|
+
aws_access_key = config['aws_access_key']
|
77
|
+
aws_secret_access_key = config['aws_secret_access_key']
|
43
78
|
|
44
|
-
|
79
|
+
options = {}
|
80
|
+
options = S33r.keys_to_symbols(config['options']) if config['options']
|
81
|
+
|
82
|
+
custom = {}
|
83
|
+
custom = S33r.keys_to_symbols(config['custom']) if config['custom']
|
84
|
+
|
85
|
+
[aws_access_key, aws_secret_access_key, options, custom]
|
45
86
|
end
|
46
87
|
|
47
|
-
#
|
88
|
+
# Wrapper round embedded client +use_ssl+ accessor.
|
48
89
|
def use_ssl?
|
49
90
|
@client.use_ssl
|
50
91
|
end
|
51
92
|
|
52
|
-
#
|
93
|
+
# Send a request over the wire.
|
94
|
+
#
|
95
|
+
# This method streams +data+ if it responds to the +stat+ method
|
96
|
+
# (as files do).
|
97
|
+
#
|
98
|
+
#-- TODO: set timeout on requests in case S3 is unresponsive
|
53
99
|
def do_request(method, path, data=nil, headers={})
|
54
100
|
req = get_requester(method, path)
|
55
101
|
req.chunk_size = @chunk_size
|
@@ -89,92 +135,97 @@ module S3
|
|
89
135
|
|
90
136
|
end
|
91
137
|
|
92
|
-
#
|
93
|
-
def do_get(path='/', headers={})
|
94
|
-
do_request('GET', path, headers)
|
95
|
-
end
|
96
|
-
|
97
|
-
# head
|
98
|
-
def do_head(path='/', headers={})
|
99
|
-
do_request('HEAD', path, nil, headers)
|
100
|
-
end
|
101
|
-
|
102
|
-
# post
|
103
|
-
def do_post(path='/', data=nil, headers={})
|
104
|
-
do_request('POST', path, data, headers)
|
105
|
-
end
|
106
|
-
|
107
|
-
# put
|
108
|
-
def do_put(path='/', data=nil, headers={})
|
109
|
-
do_request('PUT', path, data, headers)
|
110
|
-
end
|
111
|
-
|
112
|
-
# delete
|
113
|
-
def do_delete(path, headers={})
|
114
|
-
do_request('DELETE', path, nil, headers)
|
115
|
-
end
|
116
|
-
|
117
|
-
# return an instance of an appropriate request class
|
138
|
+
# Return an instance of an appropriate request class.
|
118
139
|
def get_requester(method, path)
|
119
|
-
raise
|
140
|
+
raise S33rException::UnsupportedHTTPMethod, "The #{method} HTTP method is not supported" if !(METHOD_VERBS.include?(method))
|
120
141
|
eval("HTTP::" + method[0,1].upcase + method[1..-1].downcase + ".new('#{path}')")
|
121
142
|
end
|
122
143
|
|
123
|
-
#
|
124
|
-
def
|
144
|
+
# List all buckets.
|
145
|
+
def list_buckets
|
125
146
|
do_get('/')
|
126
147
|
end
|
127
148
|
|
128
|
-
#
|
129
|
-
#
|
130
|
-
#
|
131
|
-
#
|
132
|
-
#
|
133
|
-
#
|
149
|
+
# List entries in a bucket.
|
150
|
+
#
|
151
|
+
# +query_params+: hash of options on the bucket listing request, passed as querystring parameters to S3
|
152
|
+
# (see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/).
|
153
|
+
# * <tt>:prefix => 'some_string'</tt>: restrict results to keys beginning with 'some_string'
|
154
|
+
# * <tt>:marker => 'some_string'</tt>: restict results to keys occurring lexicographically after 'some_string'
|
155
|
+
# * <tt>:max_keys => 1000</tt>: return at most this number of keys (maximum possible value is 1000)
|
156
|
+
# * <tt>:delimiter => 'some_string'</tt>: keys containing the same string between prefix and the delimiter
|
157
|
+
# are rolled up into a CommonPrefixes element inside the response
|
134
158
|
def list_bucket(bucket_name, query_params={})
|
135
159
|
if query_params[:max_keys]
|
136
160
|
max_keys = query_params[:max_keys].to_i
|
137
|
-
raise
|
161
|
+
raise S33rException::BucketListingMaxKeysError, "max_keys option to list bucket cannot be > #{BUCKET_LIST_MAX_MAX_KEYS}" \
|
138
162
|
if max_keys > BUCKET_LIST_MAX_MAX_KEYS
|
139
163
|
|
140
164
|
# take out the max_keys parameter and move it to max-keys
|
141
165
|
query_params['max-keys'] = query_params.delete(:max_keys)
|
142
166
|
end
|
143
|
-
|
167
|
+
|
168
|
+
resp = do_get("/#{bucket_name}" + generate_querystring(query_params))
|
169
|
+
bucket_listing = BucketListing.new(resp.body)
|
170
|
+
|
171
|
+
[resp, bucket_listing]
|
144
172
|
end
|
145
173
|
|
146
|
-
#
|
174
|
+
# Create a bucket.
|
147
175
|
def create_bucket(bucket_name, headers={})
|
148
176
|
do_put("/#{bucket_name}", nil, headers)
|
149
177
|
end
|
150
178
|
|
151
|
-
#
|
152
|
-
#
|
153
|
-
#
|
154
|
-
|
179
|
+
# Delete a bucket.
|
180
|
+
#
|
181
|
+
# +options+ hash can contain the following:
|
182
|
+
# * <tt>:force => true</tt>: delete all keys within the bucket then delete the bucket itself
|
183
|
+
#-- TODO: maybe delete keys matching a partial path
|
184
|
+
def delete_bucket(bucket_name, headers={}, options={})
|
185
|
+
if true == options[:force]
|
186
|
+
_, bucket_listing = list_bucket(bucket_name)
|
187
|
+
bucket_listing.contents.each_value do |obj|
|
188
|
+
delete_resource(bucket_name, obj.key)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
155
192
|
do_delete("/#{bucket_name}", headers)
|
156
193
|
end
|
157
194
|
|
158
|
-
#
|
195
|
+
# Returns true if bucket exists.
|
159
196
|
def bucket_exists?(bucket_name)
|
160
|
-
|
197
|
+
do_head("/#{bucket_name}").ok?
|
198
|
+
end
|
199
|
+
|
200
|
+
# Fetch a resource and return a fleshed-out S3Object instance.
|
201
|
+
def get_resource(bucket_name, resource_key, headers={})
|
202
|
+
do_get("/#{bucket_name}/#{resource_key}", headers)
|
161
203
|
end
|
162
204
|
|
163
|
-
#
|
205
|
+
# Put some generic resource onto S3.
|
164
206
|
def put_resource(bucket_name, resource_key, data, headers={})
|
165
207
|
do_put("/#{bucket_name}/" + "#{CGI::escape(resource_key)}", data, headers)
|
166
208
|
end
|
167
209
|
|
168
|
-
#
|
210
|
+
# Put a string onto S3.
|
169
211
|
def put_text(string, bucket_name, resource_key, headers={})
|
170
212
|
headers["Content-Type"] = "text/plain"
|
171
213
|
put_resource(bucket_name, resource_key, string, headers)
|
172
214
|
end
|
173
215
|
|
174
|
-
#
|
175
|
-
#
|
176
|
-
#
|
177
|
-
#
|
216
|
+
# Put a file onto S3.
|
217
|
+
#
|
218
|
+
# If +resource_key+ is nil, the filename is used as the key instead.
|
219
|
+
#
|
220
|
+
# +headers+ sets some headers with the request; useful if you have an odd file type
|
221
|
+
# not recognised by the mimetypes library, and want to explicitly set the Content-Type header.
|
222
|
+
#
|
223
|
+
# +options+ hash simplifies setting some headers with specific meaning to S3:
|
224
|
+
# * <tt>:render_as_attachment => true</tt>: set the Content-Disposition for this file to "attachment" and set
|
225
|
+
# the default filename for saving the file (when accessed by a web browser) to +filename+; this
|
226
|
+
# turns the file into a download when opened in a browser, rather than trying to render it inline.
|
227
|
+
#
|
228
|
+
# Note that this method uses a handle to the file, so it can be streamed in chunks to S3.
|
178
229
|
def put_file(filename, bucket_name, resource_key=nil, headers={}, options={})
|
179
230
|
# default to the file path as the resource key if none explicitly set
|
180
231
|
if resource_key.nil?
|
@@ -186,7 +237,7 @@ module S3
|
|
186
237
|
headers['Content-Disposition'] = "attachment; filename=#{File.basename(filename)}"
|
187
238
|
end
|
188
239
|
|
189
|
-
# content type is explicitly set in the headers
|
240
|
+
# content type is explicitly set in the headers, so apply to request
|
190
241
|
if headers[:content_type]
|
191
242
|
# use the first MIME type corresponding to this content type string
|
192
243
|
# (MIME::Types returns an array of possible MIME types)
|
@@ -205,14 +256,40 @@ module S3
|
|
205
256
|
end
|
206
257
|
end
|
207
258
|
|
208
|
-
#
|
209
|
-
def delete_resource(bucket_name, resource_key)
|
259
|
+
# Delete a resource from S3.
|
260
|
+
def delete_resource(bucket_name, resource_key, headers={})
|
261
|
+
do_delete("/#{bucket_name}/#{resource_key}", headers)
|
210
262
|
end
|
211
263
|
|
212
|
-
#
|
213
|
-
#
|
264
|
+
# Add any default headers which should be sent with every request from the client.
|
265
|
+
#
|
266
|
+
# +headers+ is a hash of headers already set up. Any headers passed in here
|
267
|
+
# override the defaults in +client_headers+.
|
268
|
+
#
|
269
|
+
# Returns +headers+ with the content of +client_headers+ merged in.
|
214
270
|
def add_client_headers(headers)
|
215
|
-
headers.merge!(
|
271
|
+
headers.merge!(client_headers) { |key, arg, default| arg }
|
272
|
+
end
|
273
|
+
|
274
|
+
protected
|
275
|
+
def do_get(path='/', headers={})
|
276
|
+
do_request('GET', path, headers)
|
277
|
+
end
|
278
|
+
|
279
|
+
def do_head(path='/', headers={})
|
280
|
+
do_request('HEAD', path, nil, headers)
|
281
|
+
end
|
282
|
+
|
283
|
+
def do_post(path='/', data=nil, headers={})
|
284
|
+
do_request('POST', path, data, headers)
|
285
|
+
end
|
286
|
+
|
287
|
+
def do_put(path='/', data=nil, headers={})
|
288
|
+
do_request('PUT', path, data, headers)
|
289
|
+
end
|
290
|
+
|
291
|
+
def do_delete(path, headers={})
|
292
|
+
do_request('DELETE', path, nil, headers)
|
216
293
|
end
|
217
294
|
|
218
295
|
end
|