Class: SimpleAWS::S3

Inherits:
API
  • Object
show all
Defined in:
lib/simple_aws/s3.rb

Overview

Amazon's Simple Storage Service

http://docs.amazonwebservices.com/AmazonS3/latest/API/Welcome.html

As S3 is much closer to a RESTful service than the other AWS APIs, all calls through this API are done through these five handled HTTP METHODS: GET, PUT, DELETE, POST and HEAD. When sending a request, follow exactly what is described in the AWS API docs in the link above.

So "GET Service" is

s3.get "/"

When working with a specific bucket, pass in :bucket after the path:

s3.get "/", :bucket => "bucket_name"

s3.get "/?policy", :bucket => "bucket_name"

For requests that need extra parameters, use the :params option:

s3.get "/object/name", :bucket => "bucket_name", :params => {
  "response-content-disposition" => "attachment"
}

Also use params in the cases that AWS asks for form fields, such as "POST Object".

A lot of S3 communication happens through request and response headers. To specify a certian set of headers on the request, use :headers:

s3.get "/", :bucket => "bucket_name", :headers => {
  "x-amz-security-token" => "security string"
}

Many of the PUT requests require a body of some sort, sometimes XML, sometimes JSON, and other times the raw file data. Use :body for this information. :body is expected to be either a String containing the XML or JSON information, or an object that otherwise response to #read for file uploads. This API does not build XML or JSON for you right now.

s3.put "/object/name.txt", :bucket => "bucket_name", :body => File.open()

This API does ensure that file data is uploaded as efficiently as possible, streaming file data from disc to AWS without blowing up memory. If the Content-Type header is not specified, it will be defaulted to application/octet-stream

NOTE: Like the other parts of SimpleAWS, this API does NOT try to make the AWS API better, but simply provides a cleaner, easy to use API for Ruby. As such, this API does not offer streaming downloads of file data from S3. That is up to you to implement at this time, by running a HEAD to get Content-Length then repeated GETs using the "Range:bytes" header to specify which parts to download next. You can see an example of this in samples/s3_batch_download.rb.

Quality of Life note: if you forget the leading / (forward slash) in the path of a resource when# working with a bucket, this library will catch the omission and fix the path for you. Thus, the following is also a valid call:

s3.put "object/name.txt", :bucket => "bucket_name", :body => File.open()

Raw file data in a response will be available in the #body method on the Response returned by the method call.

Instance Attribute Summary

Attributes inherited from API

#access_key, #debug_to, #region, #secret_key, #version

Instance Method Summary collapse

Methods inherited from API

#debug!, default_region, endpoint, #initialize, use_https, version

Constructor Details

This class inherits a constructor from SimpleAWS::API

Instance Method Details

#build_request(method, path, options = {}) ⇒ SimpleAWS::Request

Build a request but do not send it. Helpful for debugging.



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/simple_aws/s3.rb', line 214

def build_request(method, path, options = {})
  if options[:bucket]
    path = "/#{options[:bucket]}/#{path}".gsub("//", "/")
  end

  request = SimpleAWS::Request.new method, self.uri, path

  (options[:params] || {}).each do |k, v|
    request.params[k] = v
  end

  (options[:headers] || {}).each do |k, v|
    request.headers[k] = v
  end

  if options[:file]
    options[:body] = options.delete(:file)
  end

  signing_params = {}
  request.params.delete_if {|k, v|
    if k =~ /^response-/i
      signing_params[k] = v
      true
    end
  }

  if signing_params.length > 0
    to_add = signing_params.map {|k, v|
      "#{k}=#{v}"
    }.join("&")

    request.path = request.path + "?#{to_add}"
  end

  request.body = options[:body]

  if request.body
    request.headers["Content-Length"] = calculate_size_of(request.body).to_s

    if request.body.respond_to?(:read)
      request.headers["Content-Type"] ||= "application/octet-stream"
      request.headers["Expect"] = "100-continue"
    end

    request.headers["Content-Type"] ||= "application/x-www-form-urlencoded"
  end

  request
end

#call(method, path, options = {}) ⇒ SimpleAWS::Response

Execute an HTTP request against S3.



198
199
200
201
202
203
# File 'lib/simple_aws/s3.rb', line 198

def call(method, path, options = {})
  request = self.build_request method, path, options

  connection = SimpleAWS::Connection.new self
  connection.call finish_and_sign_request(request)
end

#delete(path, options = {}) ⇒ SimpleAWS::Response

Send a request using HTTP DELETE



169
170
171
# File 'lib/simple_aws/s3.rb', line 169

def delete(path, options = {})
  call :delete, path, options
end

#get(path, options = {}) ⇒ SimpleAWS::Response

Send a request using HTTP GET



127
128
129
# File 'lib/simple_aws/s3.rb', line 127

def get(path, options = {})
  call :get, path, options
end

#head(path, options = {}) ⇒ SimpleAWS::Response

Send a request using HTTP HEAD



183
184
185
# File 'lib/simple_aws/s3.rb', line 183

def head(path, options = {})
  call :head, path, options
end

#post(path, options = {}) ⇒ SimpleAWS::Response

Send a request using HTTP POST



141
142
143
# File 'lib/simple_aws/s3.rb', line 141

def post(path, options = {})
  call :post, path, options
end

#put(path, options = {}) ⇒ SimpleAWS::Response

Send a request using HTTP PUT



155
156
157
# File 'lib/simple_aws/s3.rb', line 155

def put(path, options = {})
  call :put, path, options
end

#uriObject



265
266
267
268
269
270
271
272
273
# File 'lib/simple_aws/s3.rb', line 265

def uri
  return @uri if @uri

  @uri = @use_https ? "https" : "http"
  @uri += "://#{@endpoint}"
  @uri += "-#{@region}" if @region
  @uri += ".amazonaws.com"
  @uri
end

#url_for(path, options = {}) ⇒ String

Build a full URL for the resource at +path+.



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/simple_aws/s3.rb', line 89

def url_for(path, options = {})
  request = build_request(:get, path, options)

  url = "#{self.uri}#{request.path}"
  sep = url =~ /\?/ ? "&" : "?"

  if request.params.any?
    params = request.params.map {|k, v| "#{k}=#{v}"}.join("&")
    url += "#{sep}#{params}"
    sep = "&"
  end

  if expires_at = options[:expires]
    # Small hack, expires is in the Date section of the
    # signing string, so we just do that here so that we don't
    # muddy up build_signature_for
    request.headers["Date"] = expires_at.to_i

    signature = "Signature=#{build_signature_for(request)}"
    key = "AWSAccessKeyId=#{self.access_key}"
    expires = "Expires=#{expires_at.to_i}"

    url += "#{sep}#{signature}&#{key}&#{expires}"
  end

  url
end