Class: Net::Amazon::S3

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/net/amazon/s3.rb,
lib/net/amazon/s3/bucket.rb,
lib/net/amazon/s3/errors.rb,
lib/net/amazon/s3/object.rb

Overview

Net::Amazon::S3

This library implements the Amazon S3 REST API (Rubyfied for your pleasure). Its usage is hopefully pretty straightforward. See below for examples.

Author

Ryan Grove ([email protected])

Version

0.1.0

Copyright

Copyright © 2006 Ryan Grove. All rights reserved.

License

New BSD License (opensource.org/licenses/bsd-license.php)

Website

wonko.com/software/net-amazon-s3

A Brief Overview of Amazon S3

Amazon S3 stores arbitrary values (objects) identified by keys and organized into buckets. An S3 bucket is essentially a glorified Hash. Object values can be up to 5 GB in size, and objects can also have up to 2 KB of metadata associated with them.

Bucket names share a global namespace and must be unique across all of S3, but object keys only have to be unique within the bucket in which they are stored.

For more details, visit s3.amazonaws.com

Installation

gem install net-amazon-s3

Examples

Create an instance of the S3 client

require 'rubygems'
require 'net/amazon/s3'

access_key_id     = 'DXM37ARQ25519H34E6W2'
secret_access_key = '43HM88c+8kYr/UeFp+shjTnzFgisO5AZzpEO06FU'

s3 = Net::Amazon::S3.new(access_key_id, secret_access_key)

Create a bucket and add an object to it

foo = s3.create_bucket('foo')
foo['bar'] = 'baz'            # create object 'bar' and assign it the
                              # value 'baz'

Upload a large file to the bucket

File.open('mybigmovie.avi', 'rb') do |file|
  foo['mybigmovie.avi'] = file
end

Download a large file from the bucket

File.open('mybigmovie.avi', 'wb') do |file|
  foo['mybigmovie.avi'].value {|chunk| file.write(chunk) }
end

Get a hash containing all objects in the bucket

objects = foo.get_objects

Get all objects in the bucket whose keys begin with “my”

my_objects = foo.get_objects('my')

Delete the bucket and everything in it

s3.delete_bucket('foo', true)

TODO

  • Owner support

  • Object metadata support

  • ACLs

  • Logging configuration

Defined Under Namespace

Classes: Bucket, Object, S3Error

Constant Summary collapse

REST_ENDPOINT =
's3.amazonaws.com'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(access_key_id, secret_access_key, options = {}) ⇒ S3

Creates and returns a new S3 client. The following options are available:

:enable_cache

Set to true to enable intelligent caching of frequently-used requests. This can improve performance, but may result in staleness if other clients are simultaneously modifying the buckets and objects in this S3 account. Default is true.

:ssl

Set to true to use SSL for all requests. No verification is performed on the server’s certificate when SSL is used. Default is true.



108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/net/amazon/s3.rb', line 108

def initialize(access_key_id, secret_access_key, options = {})
  @access_key_id     = access_key_id
  @secret_access_key = secret_access_key

  @options = {
    :enable_cache => true,
    :ssl          => true
  }

  @options.merge!(options)
  
  @cache = {}
end

Instance Attribute Details

#access_key_idObject

Returns the value of attribute access_key_id.



94
95
96
# File 'lib/net/amazon/s3.rb', line 94

def access_key_id
  @access_key_id
end

#optionsObject (readonly)

Returns the value of attribute options.



95
96
97
# File 'lib/net/amazon/s3.rb', line 95

def options
  @options
end

#secret_access_keyObject

Returns the value of attribute secret_access_key.



94
95
96
# File 'lib/net/amazon/s3.rb', line 94

def secret_access_key
  @secret_access_key
end

Instance Method Details

#bucket_exist?(bucket_name) ⇒ Boolean Also known as: has_bucket?

Returns true if a bucket with the specified bucket_name exists in this S3 account, false otherwise.

Returns:

  • (Boolean)


124
125
126
# File 'lib/net/amazon/s3.rb', line 124

def bucket_exist?(bucket_name)
  return get_buckets.has_key?(bucket_name)
end

#create_bucket(bucket_name) ⇒ Object

Creates a new bucket with the specified bucket_name and returns a Bucket object representing it.



132
133
134
135
136
# File 'lib/net/amazon/s3.rb', line 132

def create_bucket(bucket_name)
  error?(request_put("/#{bucket_name}"))
  @cache.delete(:buckets)
  return get_bucket(bucket_name)
end

#delete_bucket(bucket_name, recursive = false) ⇒ Object

Deletes the bucket with the specified bucket_name. If recursive is true, all objects contained in the bucket will also be deleted. If recursive is false and the bucket is not empty, a S3Error::BucketNotEmpty error will be raised.



142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/net/amazon/s3.rb', line 142

def delete_bucket(bucket_name, recursive = false)
  unless bucket = get_bucket(bucket_name)
    raise S3Error::NoSuchBucket, 'The specified bucket does not exist'
  end

  if recursive
    bucket.each {|object| bucket.delete_object(object.name) }
  end
  
  @cache.delete(:buckets)
  
  return true unless error?(request_delete("/#{bucket_name}"))
end

#eachObject

Iterates through the list of buckets.



157
158
159
# File 'lib/net/amazon/s3.rb', line 157

def each
  get_buckets.each {|key, value| yield key, value }
end

#error?(response) ⇒ Boolean

Raises the appropriate error if the specified Net::HTTPResponse object contains an Amazon S3 error; returns false otherwise.

Returns:

  • (Boolean)


163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/net/amazon/s3.rb', line 163

def error?(response)
  return false if response.is_a?(Net::HTTPSuccess)

  xml = REXML::Document.new(response.body)
  
  unless xml.root.name == 'Error'
    raise S3Error, "Unknown error: #{response.body}"
  end
  
  error_code    = xml.root.elements['Code'].text
  error_message = xml.root.elements['Message'].text

  if S3Error.const_defined?(error_code)
    raise S3Error.const_get(error_code), error_message
  else
    raise S3Error, "#{error_code}: #{error_message}"
  end
end

#get_bucket(bucket_name) ⇒ Object Also known as: []

Returns a Bucket object representing the specified bucket, or nil if the bucket doesn’t exist.



184
185
186
187
# File 'lib/net/amazon/s3.rb', line 184

def get_bucket(bucket_name)
  return nil unless bucket_exist?(bucket_name)
  return @cache[:buckets][bucket_name]
end

#get_bucketsObject

Gets a list of all buckets owned by this account. Returns a Hash of Bucket objects indexed by bucket name.



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/net/amazon/s3.rb', line 193

def get_buckets
  if @options[:enable_cache] && !@cache[:buckets].nil?
    return @cache[:buckets]
  end
  
  response = request_get('/')
  error?(response)
  
  xml = REXML::Document.new(response.body)
  
  buckets = {}

  xml.root.elements.each('Buckets/Bucket') do |element|
    bucket_name   = element.elements['Name'].text
    creation_date = Time.parse(element.elements['CreationDate'].text)
    
    buckets[bucket_name] = Bucket.new(self, bucket_name, creation_date)
  end
  
  return @cache[:buckets] = buckets
end

#request_delete(path, headers = {}) ⇒ Object

Sends a properly-signed DELETE request to the specified S3 path and returns a Net::HTTPResponse object.



217
218
219
220
221
222
223
224
225
226
227
# File 'lib/net/amazon/s3.rb', line 217

def request_delete(path, headers = {})
  http = Net::HTTP.new(REST_ENDPOINT, @options[:ssl] ? 443 : 80)

  http.use_ssl     = @options[:ssl]
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  
  http.start do |http|
    req = sign_request(Net::HTTP::Delete.new(path), nil, headers)
    return http.request(req)
  end
end

#request_get(path, headers = {}) ⇒ Object

Sends a properly-signed GET request to the specified S3 path and returns a Net::HTTPResponse object.

When called with a block, yields a Net::HTTPResponse object whose body has not been read; the caller can process it using Net::HTTPResponse.read_body.



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/net/amazon/s3.rb', line 235

def request_get(path, headers = {})
  http = Net::HTTP.new(REST_ENDPOINT, @options[:ssl] ? 443 : 80)

  http.use_ssl     = @options[:ssl]
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  
  http.start do |http|
    req = sign_request(Net::HTTP::Get.new(path), nil, headers)
    
    if block_given?
      http.request(req) {|response| yield response }
    else
      return http.request(req)
    end
  end
end

#request_head(path, headers = {}) ⇒ Object

Sends a properly-signed HEAD request to the specified S3 path and returns a Net::HTTPResponse object.



254
255
256
257
258
259
260
261
262
263
264
# File 'lib/net/amazon/s3.rb', line 254

def request_head(path, headers = {})
  http = Net::HTTP.new(REST_ENDPOINT, @options[:ssl] ? 443 : 80)

  http.use_ssl     = @options[:ssl]
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  
  http.start do |http|
    req = sign_request(Net::HTTP::Head.new(path), nil, headers)
    return http.request(req)
  end
end

#request_put(path, content = nil, headers = {}) ⇒ Object

Sends a properly-signed PUT request to the specified S3 path and returns a Net::HTTPResponse object.

If content is an open IO stream, the body of the request will be read from the stream.



271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/net/amazon/s3.rb', line 271

def request_put(path, content = nil, headers = {})
  http = Net::HTTP.new(REST_ENDPOINT, @options[:ssl] ? 443 : 80)
  
  http.use_ssl     = @options[:ssl]
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  
  http.start do |http|
    req = sign_request(Net::HTTP::Put.new(path), content, headers)
    
    if content.is_a?(IO)
      req.body_stream = content
    else
      req.body = content
    end
    
    response = http.request(req)
    
    return response
  end
end