Paperclip 2.3.11 throws CurrentBucketNotSpecified error for `expiring_url` - ruby-on-rails

My setup:
1) I have added a CNAME on my domain to point to S3.
assets.foo.com -> s3.amazonaws.com
2) I have a bucket called assets.foo.com on S3
3) Model code:
has_attached_file :report,
:storage => :s3,
:s3_credentials => {
:access_key_id => "xxxx",
:secret_access_key => "yyyy"},
:s3_permissions => 'private',
:s3_protocol => 'http',
:s3_host_alias => "assets.foo.com",
:url => ":s3_alias_url",
:bucket => "assets.foo.com",
:path => ":class/:attachment/:id_partition_:style.:extension"
I get the expected URL when I call the url method.
s.report.url
#http://assets.foo.com/study/report/..../abc.pdf
I get an error when try to generate an expiring URL
s.report.expiring_url
AWS::S3::CurrentBucketNotSpecified: No bucket name can be inferred from your current connection's address (`s3.amazonaws.com')
from C:/Ruby187/lib/ruby/gems/1.8/gems/aws-s3-0.6.2/lib/aws/s3/base.rb:107:in `current_bucket'
from C:/Ruby187/lib/ruby/gems/1.8/gems/aws-s3-0.6.2/lib/aws/s3/base.rb:179:in `bucket_name'
from C:/Ruby187/lib/ruby/gems/1.8/gems/aws-s3-0.6.2/lib/aws/s3/object.rb:300:in `path!'
from C:/Ruby187/lib/ruby/gems/1.8/gems/aws-s3-0.6.2/lib/aws/s3/object.rb:291:in `url_for'
from C:/Ruby187/lib/ruby/gems/1.8/gems/paperclip-2.3.11/lib/paperclip/storage/s3.rb:98:in `expiring_url'
from (irb):4
According to the user manual the bucket name is inferred if the :s3_host_alias key is specified and :url key is set to ":s3_alias_url". I have configured my model as per the instruction. I still encountered the error.
I was able to work around the problem by adding the bucket configuration, i.e.
has_attached_file :report,
:storage => :s3,
:s3_credentials => {
:access_key_id => "xxxx",
:secret_access_key => "yyyy"},
:s3_permissions => 'private',
:s3_protocol => 'http',
:s3_host_alias => "assets.foo.com",
:url => ":s3_alias_url",
:bucket => "assets.foo.com",
:path => ":class/:attachment/:id_partition_:style.:extension"
When I add the bucket configuration, the expiring_url method ignores the custom CNAME alias.
s.report.expiring_url
#http://s3.amazon.com/assets.foo.com/study/report/..../abc.pdf
Interestingly, the url function generates the expected url even when the bucket configuration is present.
s.report.url
#http://assets.foo.com/study/report/..../abc.pdf
Note: I tried various combination of CNAME alias with the same result:
assets.foo.com -> s3.amazonaws.com
assets.foo.com -> assets.foo.com.s3.amazonaws.com

The issue is that you are in a situation where it can not infer your current bucket. The documentation says that you can set the bucket name to avoid this error. However as you have realized this wont generate the correct URL.
Try setting the :url in your config to the correct value as well with the correct setting for the bucket and it should work.

This behaviour is seen ONLY when expire_url function is called when s3_host_alias is set. I monkey patched the gem to get around the issue.
Added the patch in config\initializers\paperclip.rb
module Paperclip::Storage::S3
def bucket_name_with_s3_host_alias
s3_host_alias || bucket_name_without_s3_host_alias
end
alias_method_chain :bucket_name, :s3_host_alias
def expiring_url_with_s3_host_alias
result = expiring_url_without_s3_host_alias
s3_host_alias.present? ?
result.gsub(/(\/s3.amazonaws.com)|(\.s3.amazonaws.com)/, '') : result
end
alias_method_chain :expiring_url, :s3_host_alias
end

Related

AWS::S3::Errors::PermanentRedirect on Heroku

I'm getting following error on heroku after uploading file through paperclip.
AWS::S3::Errors::PermanentRedirect (The bucket you are attempting to
access must be addressed using the specified endpoint. Please send all
future requests to this endpoint.)
This is my settings in the model
has_attached_file :profile_image,
:styles => { :myrecipes => "260x180#"},
:storage => :s3,
:s3_region => 'us-west-1',
:s3_credentials => "#{Rails.root}/config/amazon_s3.yml",
:path => "/images/:id/:style.:extension",
:url => ":s3_domain_url"
This is working on development and store image on S3 but while I'm trying on production (Heroku) I'm getting error.
To Provide the endpoint you have to do add this into your paperclip_defaults
:s3_host_name => "s3-eu-west-1.amazonaws.com"
Or you can do like this
s3_host_name: "s3-#{ENV['AWS_REGION']}.amazonaws.com"
Ref: paperclip issue

Getting S3 Credentials from the database at startup in rails

Using a combination of the rails-settings gem and paperclip, I've got my application set up such that one can specify S3 credentials in the database (through an interface in the front end) and can then declare attachments like this;
class Asset < ActiveRecord::Base
has_attached_file :attachment,
:default_url => "content/no-img.jpg",
:s3_credentials => {
:access_key_id => Settings.AWS_ACCESS_KEY_ID,
:secret_access_key => Settings.AWS_SECRET_ACCESS_KEY
}
This works great, but this isn't the only type of attachment in the application (there are other things such as client logos). I'm finding that I need to specify the S3 credentials on every one. I've already tucked some of the defaults away in my app config file;
class Application < Rails::Application
#Paperclip Defaults
config.paperclip_defaults =
{
:storage => :s3,
:bucket => "my-bucket-name"
}
And this all works great, but if I try to pull down the username and password into the app config too, I get the following error;
ActiveRecord::ConnectionNotEstablished (ActiveRecord::ConnectionNotEstablished)
A fairly self explanatory error message, but how do I get around it? If I put the config in the environment files I get the same issue, and if I put them in an initializer like this;
#Paperclip Defaults
MyApp::Application.config.paperclip_defaults =
{
:storage => :s3,
:bucket => "my-bucket-name",
:s3_credentials => {
:access_key_id => RailsSettings::Settings.AWS_ACCESS_KEY_ID,
:secret_access_key => RailsSettings::Settings.AWS_SECRET_ACCESS_KEY
}
}
Then the values don't seem to get picked up and paperclip defaults back to storing the files in my local system directory. Could anyone advise on the best approach to handling this?
EDIT
So after re-reading the documentation I discovered "After Initializers", which gets around the whole active-record not being loaded issue. I have this in my app config;
config.after_initialize do
#Paperclip Defaults
config.paperclip_defaults =
{
:storage => :s3,
:bucket => "my-bucket-name",
:s3_credentials => {
:access_key_id => RailsSettings::Settings.AWS_ACCESS_KEY_ID,
:secret_access_key => RailsSettings::Settings.AWS_SECRET_ACCESS_KEY
}
}
end
And specify this in each model;
has_attached_file :attachment,
:default_url => "content/no-img.jpg",
:path => "#{Rails.env}/:client_name/#{DateTime.now.year}/:name/:basename/:basename-:style.:extension"
yet it still isn't picking up the defaults I specify (it still tries to put them in the public/system folder)
Right, sorted. If anyone has this issue, here's the solution (you basically need to use the "initializers" method to switch active record on before you set the config);
initializer "active_record.initialize_database" do |app|
app.config.paperclip_defaults =
{
:storage => :s3,
:bucket => "my-bucket-name",
:s3_credentials => {
:access_key_id => RailsSettings::Settings.AWS_ACCESS_KEY_ID,
:secret_access_key => RailsSettings::Settings.AWS_SECRET_ACCESS_KEY
}
}
end

Rails download S3 file

I'm uploading files to S3 with paperclip, and now I would like to download them from the same app. So I'm doing what lots of pages says, but if I use 'aws-sdk' it says that AWS::S3::S3Object method 'find' doesn't exist, and If I use 'aws-s3' gem, it says that I need to use 'aws-sdk'.
In controller I'm calling:
aws_object = AWS::S3::S3Object.find #component.folder.path, 'bucket-name'
send_data(aws_object.value, :type => #component.folder_content_type)
EDIT:
My model looks like:
attr_accessible :folder
has_attached_file :folder,
:path => ":rails_root/data/folders/:id/:basename.:extension",
:storage => :s3,
:s3_credentials => {
:bucket => "my-bucket-name",
:access_key_id => "XXXXXXXXX",
:secret_access_key => "XXXXXXXXX"
}
This worked for me:
http://trevorturk.com/2008/12/11/easy-upload-via-url-with-paperclip/
There's a example to download too.
The secret was ".read" :
data = open(asset.uploaded_file.url)
send_data data.read, :type => data.content_type, :x_sendfile => true,:filename => asset.file_name

Rails MIME::Types.type_for(photoshop_1354320001.psd) - MIME Type not found?

I'm using Rails 3, Paperclip(3.3.0), aws-sdk (1.7.1).
My paperclip attachments are being stored securely on S3.
attachment.rb
has_attached_file :attachment,
:storage => :s3,
:s3_credentials => "#{Rails.root}/config/s3.yml",
:s3_protocol => 'https',
:s3_permissions => :private, # Sets the file, not the folder as private in S3
:use_timestamp => false,
:default_style => :original, # NEEDS to be original or download_url method below wont work
:default_url => '/images/:attachment/default_:style.png',
:path => "/:rails_env/private/s/:s_id/uuploaded_files/:basename.:extension"
In order to download the files I generate a secure URL like so:
def authenticated_url(style = nil, expires_in = 1.hour)
mime_type = MIME::Types.type_for(self.attachment_file_name)[0]
attachment.s3_object(style).url_for(:read, :secure => true, :response_content_type => mime_type.to_s, :expires => expires_in).to_s
end
The problem is for PSDs: This is return empty:
Rails MIME::Types.type_for('photoshop_1354320001.psd')
In the code it looks like:
mime_type = MIME::Types.type_for(self.attachment_file_name)[0]
It works for other files fine but not PSDs. Any idea why and how to resolve?
Thanks
Sure. MIME::Types lets you specify custom types.
Stick this into an initializer
# Not quite sure what the appropriate MIMEtype for PSDs are,
# but this is the gist of it.
# .PSB is a larger version of .PSD supporting up to 300000x300000 px
psd_mime_type = MIME::Type.new('image/x-photoshop') do |t|
t.extensions = %w(psd psb)
t.encoding = '8bit'
end
MIME::Types.add psd_mime_type
Now MIME::Types.type_for "test.psd" should give you "image/x-photoshop".

How to use rails plus paperclip and fog in localhost

I'm trying to setup my development environment to store and fetch images in local host.
I've manage to save the images on the correct path, but I can't find a way to load the page and retrieve them from the assets pipeline.
I have this on my model:
has_attached_file :cover, :styles => {:small => '80x80'},
:storage => :fog,
:fog_credentials => {:provider => "Local",
:local_root => "#{Rails.root}/public"},
:fog_directory => 'system/migos',
:fog_host => "http://localhost:3000/assets",
:default_url => '/assets/missing/:attachment/missing_:class_:style.png',
:path => ':rails_env/:class/:attachment/:id_partition/:style/:filename'
and the file gets saved correctly to:
public/system/migos/development/workgroups/covers/000/000/011/small/logo.png
When loading the page, it tries to fetch the file from here:
/assets/localhost/development/workgroups/covers/000/000/011/small/logo.png?1346598225
and fails.
What am I missing here?
has_attached_file :photo,
:url => "/assets/vehicles/:id/:style/:basename.:extension",
:path => ":rails_root/public/assets/vehicles/:id/:style/:basename.:extension"
Try the code from above, check the URL and PATH, it will return the next image address:
http://localhost:3000/assets/vehicles/1/original/72854906.jpg?1346092386
the folder structure is assets/vehicles/1/original.

Resources