Paperclip is not generating url properly for CNAME - ruby-on-rails

I'm tying to get a cloudfront distribution wired up with a custom domain to paperclip but it's not generating my url properly. Here is my configuration:
# initializers/paperclip_defaults.rb
Paperclip::Attachment.default_options.merge!({
:storage => :s3,
:s3_credentials => "#{Rails.root}/config/amazon_s3.yml",
:bucket => "my-image-bucket",
:path => "production/attachments/:attachment/:id/:style/:basename.:extension",
:url => ":s3_host_alias",
:s3_headers => {'Cache-Control' => 'max-age=2147483648'},
:s3_protocol => :https,
:s3_host_alias => "cdn.mydomain.com",
})
According to the docs and every blog post I've read this should be correct. The problem is that it winds up generating this:
https://s3.amazonaws.com/my-image-bucket/production/products/images/44/original/my-image.jpg
The only way I've gotten it to use the CNAME is by specifying s3_host_name which according to the documentation is only used for the Tokyo region. And this also breaks uploads. Any ideas as to what I'm doing wrong?
I'm using Paperclip 3.5.2.
UPDATE
I'm debugging inside attachment.rb#url and here's what I'm seeing. Paperclip::Attachment.default_options[:url] returns cdn.mydomain.com which is correct. However, #options[:url] is returning :s3_path_url instead of s3_host_alias. For some reason that default option is not being merged in properly. However the s3_host_alias option is since #options[:s3_host_alias] is returning cdn.mydomain.com as expected.

So it turns out that :s3_alias_url is the correct token to use for the url and not :s3_host_alias. Not sure where that's documented though. Had to look at s3.rb to figure it out.

Related

images uploaded to aws, but can't be viewed in the view

I have just integrated AWS with my rails/heroku app and I am using paperclip. I am able to upload files (photo's) and see them in AWS, however they are not showing up in the view. I am not getting any errors, and have not found a working solution in other posts.
It seems I am able to view the image in a browser, and that permissions are set to public:
I suspect that I may have my region wrong, in the url of my aws dashboard the region says region=us-west-2 yet googling and reading through other forums and posts on SO leads me to believe that if I am in the US my region should be set to us-east-1 - currently I have it set to the latter. Not sure if this is the problem.
Here is a link to the image on AWS:https://s3.amazonaws.com/giving-tree-images/avatars/1/medium/02108_navajoland_1440x900.jpg
here is the code pertaining to aws/paperclip in my model:
:storage => :s3,
# :s3_host_name => "s3-us-east-1.amazonaws.com",
:s3_credentials => {
:bucket => ENV['S3_BUCKET_NAME'],
:access_key_id => ENV['AWS_ACCESS_KEY_ID'],
:secret_access_key => ENV['AWS_SECRET_ACCESS_KEY']
},
:path => ":rails_root/public/system/:attachment/:id/:style/:filename",
:default_url => "default_img.png"
and I am displaying in the view like this:
<%= image_tag #user.avatar.url(:medium) %>
I would love it if someone could point me in the right direction... Any additional info needed, please let me know!
I also faced the same issue, so this is how I solved it:
Create a new initializer file i.e. config/initializers/paperclip.rb
Add following in this file
Paperclip::Attachment.default_options[:url] = ':s3_domain_url'
Paperclip::Attachment.default_options[:s3_host_name] = 's3-us-west-2.amazonaws.com'
To get details about your region, go to aws
Amazon Web Service - Check your host name
Note : Replace s3-us-west-2.amazonaws.com with your own host name
Reference : https://devcenter.heroku.com/articles/paperclip-s3
Have you enable the read permission to everyone?
It gave me access-denied error when accessed to the link.

Rails 4 + Paperclip + S3: Changing endpoint to access an S3 bucket in User model?

Should be a pretty simple fix to this question, I think, but I can't seem to get it to work. I have a Rails 4 app, a User model with a photograph attribute setup with Paperclip, and I have it linked to S3.
Here's the User model:
has_attached_file :photograph,
:styles => { :medium => "300x300>", :thumb => "100x100>" },
:storage => :s3,
:bucket => " my-bucket-name ",
:default_url => '/images/:attachment/missing_:style.png',
:s3_credentials => S3_CREDENTIALS
The image gets added to my S3 bucket just fine, but when I try to render the picture with <%= image_tag #user.photograph.url %>, it doesn't show up. Upon further inspection, the image URL is:
http://s3.amazonaws.com/my-bucket-name/users/photographs/000/000/001/original/20121103_132556.jpg?1388619625
If I follow this URL in a browser, I see an XML file as follows:
<Error>
<Code>PermanentRedirect</Code>
<Message>
The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.
</Message>
<RequestId> ... </RequestId>
<Bucket>my-bucket-name</Bucket>
<HostId>
...
</HostId>
<Endpoint>my-bucket-name.s3.amazonaws.com</Endpoint>
</Error>
Consequently, when I follow the url http://my-bucket-name.s3.amazonaws.com/actives/photographs/000/000/001/original/20121103_132556.jpg?1388619625 in a browser, I see the picture just fine.
How do I change the endpoint in my User model? What is the normal way to handle this? I must add that my S3 bucket is the Northern California region. Thanks.
Solved! Just added these two lines in my User model:
:url =>':s3_domain_url',
:path => '/:class/:attachment/:id_partition/:style/:filename',
Sometimes the problem is that your bucket is not in the us server (amazon's default).
You can change the endpoint using this
:s3_host_name => 'your_host_name'
You can find your hostname in the following link under the endpoint column Amazon endpoints
If you don't know your bucket region, you can find it in bucket properties.

using Amazon S3 with the paperclip gem

Is it good practice to simply post my access key id/secret access key directly into the model? For example...
has_attached_file :pic, :s3_credentials => {
:bucket => 'MY_BUCKET_NAME',
:path => ":rails_root/public/system/:attachment/:id/:style/:filename",
:url => "/system/:attachment/:id/:style/:filename",
:access_key_id => "12345Secret#blahblah",
:secret_access_key => "12345###"
}
This actually works, however, when I duplicate the image to another model...it doesnt upload.
i.e. #other_user.pic = #user.pic (to copy the image to the other User)
For instance if the image that works is: S3Amazon/pic/01.working.jpg
the copied image has a one number increase in the file like so: S3Amazon/pic/02.working.jpg
This image does not open, been searching all week for a way to make this work.
Try
#other_user.pic = #user.pic
#other_user.save!
And make sure, if you're doing this from the console, that you do #other_user.reload before checking urls

Paperclip 2.3.11 throws CurrentBucketNotSpecified error for `expiring_url`

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

Rails user-conditional statement in model

I'm working on a Rails app using attachment_ fu and Amazon S3 storage. Is it possible to make the :s3_ access (a part of the has_attachment options) conditional based on a user input when creating the object. I would like the user to be able if to select if the attachment is authenticated-read or public-read. Is this possible and how would you setup the conditional statement?
My model looks like this:
has_attachment :content_type => :image,
:storage => :s3,
:size => 0.kilobytes..6144.kilobytes,
:processor => 'Rmagick',
:resize_to => '650x500>',
:thumbnails => { :thumb => '75x75!' },
:s3_access => ( [[conditional]] ? 'authenticated-read' : 'public-read' )
Obviously [[conditional]] is what I'm looking to replace, but I don't know how to correctly setup a conditional for the item based off user action in the model. Maybe this is the wrong time of conditional for this type statement. Any suggestions?
James, I've done this before. I was digging through my old code, trying to find a sample I could show here, but I can't seem to find it. Old client, code is probably gone, etc...
You are on the right track though. Remember, 'has_attachment' is just a method call with a hash for the parameters, so if you treat it as such, you can adjust which hash values are ultimately sent to the method.
Edit - Here is a code sample -
production = ENV['RAILS_ENV'] == 'production'
has_attachment :content_type => :image,
:max_size => 1.megabyte,
:resize_to => '800x600>',
:thumbnails => { :thumb => '146x146>', :small => '75x75' },
# skip s3 for local development and testing
:storage => (production ? :s3 : :file_system),
:path_prefix => (production ? 'comment_images' : 'public/comment_images')
In the above example, I wanted to store uploads locally while developing, but switch to S3 when in production. So you are very close to where you need to be...

Resources