0
@@ -3,50 +3,58 @@ module Technoweenie # :nodoc:
0
# = AWS::S3 Storage Backend
0
- # Enables use of
Amazon's Simple Storage Service (http://aws.amazon.com/s3) as a storage mechanism
0
+ # Enables use of
{Amazon's Simple Storage Service}[http://aws.amazon.com/s3] as a storage mechanism
0
- # Requires the
AWS::S3 Library for S3 by Marcel Molina Jr. (http://amazon.rubyforge.org) installed either
0
+ # Requires the
{AWS::S3 Library}[http://amazon.rubyforge.org] for S3 by Marcel Molina Jr. installed either
0
# as a gem or a as a Rails plugin.
0
# Configuration is done via <tt>RAILS_ROOT/config/amazon_s3.yml</tt> and is loaded according to the <tt>RAILS_ENV</tt>.
0
- # The minimum connection options that you must specify are
your access key id and your secret access key.
0
+ # The minimum connection options that you must specify are
a bucket name, your access key id and your secret access key.
0
# If you don't already have your access keys, all you need to sign up for the S3 service is an account at Amazon.
0
# You can sign up for S3 and get access keys by visiting http://aws.amazon.com/s3.
0
# Example configuration (RAILS_ROOT/config/amazon_s3.yml)
0
- # secret_access_key: AbCDEfGHiJKlmNOPQRS1
0
- # access_key_id: 1234567891abcdeFGHI/JKL+MnoPQrsT123UvwX4
0
- # bucket_prefix: appname_development
0
+ # bucket_name: appname_development
0
+ # access_key_id: <your key>
0
+ # secret_access_key: <your key>
0
- # secret_access_key: AbCDEfGHiJKlmNOPQRS1
0
- # access_key_id: 1234567891abcdeFGHI/JKL+MnoPQrsT123UvwX4
0
- # bucket_prefix: appname_test
0
+ # bucket_name: appname_test
0
+ # access_key_id: <your key>
0
+ # secret_access_key: <your key>
0
- # secret_access_key: AbCDEfGHiJKlmNOPQRS1
0
- # access_key_id: 1234567891abcdeFGHI/JKL+MnoPQrsT123UvwX4
0
- # bucket_prefix: appname
0
+ # bucket_name: appname
0
+ # access_key_id: <your key>
0
+ # secret_access_key: <your key>
0
- # === Required
arguments
0
+ # === Required
configuration parameters
0
# * <tt>:access_key_id</tt> - The access key id for your S3 account. Provided by Amazon.
0
# * <tt>:secret_access_key</tt> - The secret access key for your S3 account. Provided by Amazon.
0
- # * <tt>:bucket_
prefix</tt> - The string prefix to assign to each bucket. Used to create unique bucket names in the format <tt>#{bucket_prefix}_#{table_name}</tt>.
0
+ # * <tt>:bucket_
name</tt> - A unique bucket name (think of the bucket_name as being like a database name).
0
# If any of these required arguments is missing, a MissingAccessKey exception will be raised from AWS::S3.
0
- # === Optional arguments
0
+ # == About bucket names
0
+ # Bucket names have to be globaly unique across the S3 system. And you can only have up to 100 of them,
0
+ # so it's a good idea to think of a bucket as being like a database, hence the correspondance in this
0
+ # implementation to the development, test, and production environments.
0
+ # The number of objects you can store in a bucket is, for all intents and purposes, unlimited.
0
- # * <tt>:server</tt> - The server to make requests to. You can use this to specify your bucket in the subdomain, or your own domain's cname if you are using virtual hosted buckets. Defaults to <tt>s3.amazonaws.com</tt>.
0
+ # === Optional configuration parameters
0
+ # * <tt>:server</tt> - The server to make requests to. Defaults to <tt>s3.amazonaws.com</tt>.
0
# * <tt>:port</tt> - The port to the requests should be made on. Defaults to 80 or 443 if <tt>:use_ssl</tt> is set.
0
- # * <tt>:use_ssl</tt> -
Whether requests should be made over SSL. If set to true, <tt>:port</tt> will be implicitly set to 443, unless specified otherwise. Defaults to false.
0
+ # * <tt>:use_ssl</tt> -
If set to true, <tt>:port</tt> will be implicitly set to 443, unless specified otherwise. Defaults to false.
0
@@ -56,79 +64,200 @@ module Technoweenie # :nodoc:
0
# has_attachment :storage => :s3
0
- # Of course, all the usual configuration options apply:
0
+ # === Customizing the path
0
+ # By default, files are prefixed using a pseudo hierarchy in the form of <tt>:table_name/:id</tt>, which results
0
+ # in S3 urls that look like: http(s)://:server/:bucket_name/:table_name/:id/:filename with :table_name
0
+ # representing the customizable portion of the path. You can customize this prefix using the <tt>:path_prefix</tt>
0
+ # class Photo < ActiveRecord::Base
0
+ # has_attachment :storage => :s3, :path_prefix => 'my/custom/path'
0
+ # Which would result in URLs like <tt>http(s)://:server/:bucket_name/my/custom/path/:id/:filename.</tt>
0
+ # By default, files are stored on S3 with public access permissions. You can customize this using
0
+ # the <tt>:s3_access</tt> option to <tt>has_attachment</tt>. Available values are
0
+ # <tt>:private</tt>, <tt>:public_read_write</tt>, and <tt>:authenticated_read</tt>.
0
+ # Of course, all the usual configuration options apply, such as content_type and thumbnails:
0
- # has_attachment :storage => :s3, :content_type => ['application/pdf', :image], :resize_to => 'x50'
0
- # has_attachment :storage => :s3, :thumbnails => { :thumb => [50, 50], :geometry => 'x50' }
0
+ # class Photo < ActiveRecord::Base
0
+ # has_attachment :storage => :s3, :content_type => ['application/pdf', :image], :resize_to => 'x50'
0
+ # has_attachment :storage => :s3, :thumbnails => { :thumb => [50, 50], :geometry => 'x50' }
0
+ # === Accessing S3 URLs
0
+ # You can get an object's URL using the s3_url accessor. For example, assuming that for your postcard app
0
+ # you had a bucket name like 'postcard_world_development', and an attachment model called Photo:
0
+ # @postcard.s3_url # => http(s)://s3.amazonaws.com/postcard_world_development/photos/1/mexico.jpg
0
+ # The resulting url is in the form: http(s)://:server/:bucket_name/:table_name/:id/:file.
0
+ # The optional thumbnail argument will output the thumbnail's filename (if any).
0
+ # Additionally, you can get an object's base path relative to the bucket root using
0
+ # @photo.file_base_path # => photos/1
0
+ # And the full path (including the filename) using <tt>full_filename</tt>:
0
+ # @photo.full_filename # => photos/
0
+ # Niether <tt>base_path</tt> or <tt>full_filename</tt> include the bucket name as part of the path.
0
+ # You can retrieve the bucket name using the <tt>bucket_name</tt> method.
0
- class S3RequiredLibraryNotFound < StandardError; end
0
- class S3ConfigFileNotFound < StandardError; end
0
- class S3BucketExists < StandardError; end
0
+ class S3RequiredLibraryNotFoundError < StandardError; end
0
+ class S3ConfigFileNotFoundError < StandardError; end
0
def self.included(base) #:nodoc:
0
+ mattr_reader :bucket_name, :s3_config
0
- raise S3RequiredLibraryNotFound
.new('AWS::S3 could not be loaded. Try installing with sudo gem i aws-s3, or see http://amazon.rubyforge.org for more information')
0
+ raise S3RequiredLibraryNotFound
Error.new('AWS::S3 could not be loaded')
0
@@s3_config = YAML.load_file(RAILS_ROOT + '/config/amazon_s3.yml')[ENV['RAILS_ENV']].symbolize_keys
0
- raise S3ConfigFileNotFound
.new('File RAILS_ROOT/config/amazon_s3.yml not found')
0
+ raise S3ConfigFileNotFound
Error.new('File RAILS_ROOT/config/amazon_s3.yml not found')
0
- @@bucket = [@@s3_config.delete(:bucket_prefix), base.table_name].join('_')
0
- mattr_reader :s3_config, :bucket
0
+ @@bucket_name = s3_config[:bucket_name]
0
+ Base.establish_connection!(
0
+ :access_key_id => s3_config[:access_key_id],
0
+ :secret_access_key => s3_config[:secret_access_key],
0
+ :server => s3_config[:server],
0
+ :port => s3_config[:port],
0
+ :use_ssl => s3_config[:use_ssl]
0
- AWS::S3::Base.establish_connection!(s3_config)
0
- find_or_create_bucket(bucket)
0
+ # Bucket.create(@@bucket_name)
0
base.before_update :rename_file
0
- def self.find_or_create_bucket(name)
0
- AWS::S3::Bucket.find(name)
0
- rescue AWS::S3::NoSuchBucket
0
- AWS::S3::Bucket.create(name)
0
- rescue AWS::S3::AccessDenied
0
- raise S3BucketExists.new("Bucket name already exists: #{name}. Use a different bucket_prefix in RAILS_ROOT/config/amazon_s3.yml")
0
+ # Overwrites the base filename writer in order to store the old filename
0
+ @old_filename = filename unless filename.nil? || @old_filename
0
+ write_attribute :filename, sanitize_filename(value)
0
+ # The attachment ID used in the full path of a file
0
+ def attachment_path_id
0
+ ((respond_to?(:parent_id) && parent_id) || id).to_s
0
+ # The pseudo hierarchy containing the file relative to the bucket name
0
+ # Example: <tt>:table_name/:id</tt>
0
+ File.join(attachment_options[:path_prefix], attachment_path_id)
0
+ # The full path to the file relative to the bucket name
0
+ # Example: <tt>:table_name/:id/:filename</tt>
0
+ def full_filename(thumbnail = nil)
0
+ File.join(base_path, thumbnail_name_for(thumbnail))
0
- # Generates an S3 URL for the file in the form of: http(s)://<tt>{server}</tt>/<tt>{bucket_name}</tt>/<tt>{file_name}</tt>
0
- # The <tt>{server}</tt> variable defaults to <tt>AWS::S3 URL::DEFAULT_HOST</tt> (http://s3.amazonaws.com) and can be
0
- # set using the configuration parameters in <tt>RAILS_ROOT/config/amazon_s3.yml</tt>
0
+ # All public objects are accessible via a GET request to the S3 servers. You can generate a
0
+ # url for an object using the s3_url method.
0
- # Example usage: <tt>image_tag(@photo.s3_url)</tt>
0
+ # The resulting url is in the form: <tt>http(s)://:server/:bucket_name/:table_name/:id/:file</tt> where
0
+ # the <tt>:server</tt> variable defaults to <tt>AWS::S3 URL::DEFAULT_HOST</tt> (s3.amazonaws.com) and can be
0
+ # set using the configuration parameters in <tt>RAILS_ROOT/config/amazon_s3.yml</tt>.
0
+ # The optional thumbnail argument will output the thumbnail's filename (if any).
0
def s3_url(thumbnail = nil)
0
- s3_config[:use_ssl] ? 'https://' : 'http://' + (s3_config[:server] || AWS::S3::DEFAULT_HOST) + '/' + bucket + '/' + thumbnail_name_for(thumbnail)
0
+ protocol, hostname = s3_config[:use_ssl] ? 'https://' : 'http://', s3_config[:server] || DEFAULT_HOST
0
+ protocol + File.join(hostname, bucket_name, full_filename(thumbnail))
0
alias :public_filename :s3_url
0
+ # All private objects are accessible via an authenticated GET request to the S3 servers. You can generate an
0
+ # authenticated url for an object like this:
0
+ # @photo.authenticated_s3_url
0
+ # By default authenticated urls expire 5 minutes after they were generated.
0
+ # Expiration options can be specified either with an absolute time using the <tt>:expires</tt> option,
0
+ # or with a number of seconds relative to now with the <tt>:expires_in</tt> option:
0
+ # # Absolute expiration date (October 13th, 2025)
0
+ # @photo.authenticated_s3_url(:expires => Time.mktime(2025,10,13).to_i)
0
+ # # Expiration in five hours from now
0
+ # @photo.authenticated_s3_url(:expires_in => 5.hours)
0
+ # You can specify whether the url should go over SSL with the <tt>:use_ssl</tt> option.
0
+ # By default, the ssl settings for the current connection will be used:
0
+ # @photo.authenticated_s3_url(:use_ssl => true)
0
+ # Finally, the optional thumbnail argument will output the thumbnail's filename (if any):
0
+ # @photo.authenticated_s3_url('thumbnail', :expires_in => 5.hours, :use_ssl => true)
0
+ def authenticated_s3_url(*args)
0
+ thumbnail = args.first.is_a?(String) ? args.first : nil
0
+ options = args.last.is_a?(Hash) ? args.last : {}
0
+ S3Object.url_for(full_filename(thumbnail), bucket_name, options)
0
write_to_temp_file current_data
0
-
AWS::S3::S3Object.value filename, bucket0
+
S3Object.value full_filename, bucket_name0
- #
Destroys the file. Called in the after_destroy callback
0
+ #
Called in the after_destroy callback
0
-
AWS::S3::S3Object.delete filename, bucket0
+
S3Object.delete full_filename, bucket_name0
return unless @old_filename && @old_filename != filename
0
- AWS::S3::S3Object.rename(@old_filename, filename, bucket, :access => attachment_options[:s3_access])
0
+ old_full_filename = File.join(base_path, @old_filename)
0
+ :access => attachment_options[:s3_access]
0
- # Saves the file to S3
0
- AWS::S3::S3Object.store(filename, temp_data, bucket, :content_type => content_type, :access => attachment_options[:s3_access]) if save_attachment?
0
+ :content_type => content_type,
0
+ :access => attachment_options[:s3_access]
Comments
No one has commented yet.