I am getting a AWS::S3::NoConnectionEstablished exception when trying to download a file using paperclip + s3. I can fire up s3sh and create a connection just fine with the s3 credentials in my config. What is the best next step I can take to debug this issue? This is what my model looks like:
has_attached_file :file,
:storage => :s3,
:s3_permssions => :private,
:path => lambda { |attachment| ":id_partition/:basename.:extension" },
:url => lambda { |attachment| "products/:id/:basename.:extension" },
:s3_credentials => "#{Rails.root}/config/amazon_s3.yml",
:bucket => "products.mycompany.com"
And the error occurs here:
def temporary_s3_url(options={})
options.reverse_merge! :expires_in => 10.minutes #, :use_ssl => true
hard_url = AWS::S3::S3Object.url_for file.path, file.options[:bucket], options
# Use our vanity URL
hard_url.gsub("http://s3.amazonaws.com/products.mycompany.com","http://products.mycompany.com")
end
I tried hard coding a connection as the first line in the temporary_s3_url method but I get a "bucket not found" error. I think the problem is definitely that paperclip is having a problem initializing my s3 configuration.
Remember that storing to S3 is not dependable--the connection can be lost, the store fail before completing, etc.
I created my own library routines that attempt to do the store, but catch various errors. For the no connection error, I reconnect. For other storage errors, I retry (up to three times). You may also want to wait a second between retries.
Added
Below is the library routine I use for AWS calls.
You'd need to add/modify the rescue clauses to catch the errors that you're experiencing. Your connection_reset and error reporting methods will also be specific to your sw.
# Usage example:
# aws_repeat("Storing #{bucket}/#{obj}"){
# AWS::S3::S3Object.store(obj, data, bucket, opt)}
def aws_repeat(description = nil)
# Calls the block up to 3 times, allowing for AWS connection reset problems
for i in 1..3
begin
yield
rescue Errno::ECONNRESET => e
ok = false
ActiveRecord::Base.logger.error \
"AWS::S3 *** Errno::ECONNRESET => sleeping"
sleep(1)
if i == 1
# reset connection
connect_to_aws # re-login in to AWS
ActiveRecord::Base.logger.error \
"AWS::S3 *** Errno::ECONNRESET => reset connection"
end
else
ok = true
break
end
end
unless ok
msg = "AWS::S3 *** FAILURE #{description.to_s}"
ActiveRecord::Base.logger.error msg
security_log(msg)
end
ok
end
############################################
############################################
def connect_to_aws
# load params. Cache at class (app) level
##s3_config_path ||= RAILS_ROOT + '/config/amazon_s3.yml'
##s3_config ||=
YAML.load_file(##s3_config_path)[ENV['RAILS_ENV']].symbolize_keys
AWS::S3::Base.establish_connection!(
:access_key_id => ##s3_config[:access_key_id],
:secret_access_key => ##s3_config[:secret_access_key],
:server => ##s3_config[:server],
:port => ##s3_config[:port],
:use_ssl => ##s3_config[:use_ssl],
:persistent => false # from http://www.ruby-forum.com/topic/110842
)
true
end
I have paperclip with S3 and Heroku on two apps. This is what worked for me:
In your mode:
has_attached_file :image,
:styles => { :thumb => "250x250>" },
:storage => :s3, :s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
:path => "username/:attachment/:style/:id.:extension"
in config/s3.yml
development:
bucket: name
access_key_id: xyz
secret_access_key: xyz
test:
bucket: name
access_key_id: xyz
secret_access_key: xyz
production:
bucket: name
access_key_id: xyz
secret_access_key: xyz
and of course in your environment.rb you need to have the gem included or however you include gems.
Related
I have been scouring through SO posts for the last day and a half, and have asked another question related to this issue here, and have yet to come up with a solution. I am unable to render my images, which are being correctly uploaded to AWS in my view using <%= image_tag #user.avatar.url(:medium) %> as specified in the documentation.
Looking in my DB, all of the fields related to the avatar are indeed populated with:
avatar_file_name => meditation.jpg
avatar_content_type => image/jpeg
avatar_file_size => 109992
avatar_updated_at => 2016-08-04 06:48:31.361434
and the image can be viewed in the browser by double clicking on the image's link in my amazon bucket: https://s3.amazonaws.com/giving-tree-images/Users/sgau677/giving_tree/public/avatars/12/medium/meditation.jpg.jpg
As you can see, for some reason a double file extension is being appended, which is a separate issue (that I welcome feedback on) but which does not seem to be the issue, as I am not able to render in the view uploaded images with a single file extension either.
So with the images being uploaded, the db fields being populated, and no errors - I am at a loss as to why I cannot render them in the view. I suspect the issue is either 1) the path -which looks like this in my model:
has_attached_file :avatar,
:styles => { medium: "300x300#", thumb: "100x100#" },
:convert_options => {
:thumb => "-quality 75 -strip" },
:s3_host_name => "s3.amazonaws.com",
:path => ":rails_root/public/:attachment/:id/:style/:filename.:extension",
:url => ":attachment/:id/:style/:filename.:extension",
:default_url => "default_img.png",
:storage => :s3,
:s3_credentials => {
:bucket => 'giving-tree-images',
:access_key_id => ENV['AWS_ACCESS_KEY_ID'],
:secret_access_key => ENV['AWS_SECRET_ACCESS_KEY']
},
:s3_permissions => {
:original => 'public-read'
}
validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\Z/
attr_accessor :avatar_file_name, :avatar_content_type, :avatar_file_size, :avatar_updated_at
Which as you can see seems to be correct (unless I should be taking out the :rails_root bit in my model?)
or 2) I need to somehow save the location of the images in a separate model (which I have seen other ppl do, but am not sure it is required, as others seem not to have done this - granted they may just not have mentioned that they did). This being a step has not been in any of the docs I have read however.
3) I also wonder if there is possibly a separate process that I need to be doing in development as opposed to production?
or 4) I am using rails 4.2.3 and have read in comments that paperclip does not support versions of aws > 2.00 - however the Heroku docs - instructed to use 2.3...so perhaps this is the issue?
gem "paperclip", "~> 5.0.0"
gem 'aws-sdk', '~> 2.3'
I have also set up, in following other posts, a config/asw.yml file that looks like this:
development:
access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
production:
access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
although I am not sure it is being read (where the ENV var's are stored in secrets.yml, as well as both locally and on heroku via terminal) and a config/s3.yml file that looks like this:
include 'secrets.yml'
development:
access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
bucket: <%= ENV["S3_BUCKET_NAME"]%>
production:
access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
bucket: <%= ENV["S3_BUCKET_NAME"]%>
an initializers/s3.rb
if Rails.env == "production"
# set credentials from ENV hash
S3_CREDENTIALS = { :access_key_id => ENV['AWS_ACCESS_KEY_ID'], :secret_access_key => ENV['AWS_SECRET_ACCESS_KEY'], :bucket => "giving-tree-images"}
else
# get credentials from YML file
S3_CREDENTIALS = Rails.root.join("config/s3.yml")
end
and modified my both environments/development.rb and my environments/production.rb to include:
config.paperclip_defaults = {
storage: :s3,
:s3_host_name => "s3.amazonaws.com",
s3_credentials: {
bucket: ENV.fetch('S3_BUCKET_NAME'),
access_key_id: ENV.fetch('AWS_ACCESS_KEY_ID'),
secret_access_key: ENV.fetch('AWS_SECRET_ACCESS_KEY'),
s3_region: ENV.fetch('AWS_REGION'),
}
}
...as per multiple other posts combined suggested. It is quite possible I do not need all of this, so if anyone can help as to what I should be getting rid of, and why I cannot render images in the view - I would REALLY appreciate it as this is maddening.
You are putting extension after :filename for both the :path and the :url -- but you shouldn't.
:filename includes the extension. That is why you are getting the extension twice.
You have this in your model:
:path => ":rails_root/public/:attachment/:id/:style/:filename.:extension",
:url => ":attachment/:id/:style/:filename.:extension",
and after you remove .:extension, you should have this:
:path => ":rails_root/public/:attachment/:id/:style/:filename",
:url => ":attachment/:id/:style/:filename",
I have a Rails 4 app with Paperclip running on Heroku. I recently started encountering a problem that is preventing me from updating any models with attached images. For example, any time I make a change through my site's CMS system to my Company model, the changes are not saved. I've tried changing only the image, the image and various other attributes, and only non-image attributes. It's as if the #company.save is never called in my controller's update action.
I found this issue report and tried the various solutions suggested, but nothing worked.
Here is my Company model:
class Company < ActiveRecord::Base
has_attached_file :logo, :styles => { :medium => "300x300>", :thumb => "100x100>" }
validates_attachment_content_type :logo, :content_type => /\Aimage\/.*\Z/
end
In my logs, I get the following error:
[AWS S3 404 0.022711 0 retries] head_object(:bucket_name=>"myApp",:key=>"companies/profile_photos/000/000/118/original/2015-09-05_19.05.34.jpg") AWS::S3::Errors::NoSuchKey No Such Key
I verified that my S3 credentials are set using heroku config and that they are valid.
I'm not certain, but I suspect that this may have something to do with my recent addition of the following buildpacks:
=== myApp Buildpack URLs
1. https://github.com/bobbus/image-optim-buildpack.git
2. https://github.com/heroku/heroku-buildpack-ruby.git
I've not been able to reproduce the problem on localhost.
Update
I've since opened up an issue report on this problem.
Here is my config.paperclip_defaults block in production.rd
config.paperclip_defaults = {
:storage => :s3,
:s3_protocol => :https,
:s3_host_name => 's3.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']
}
}
Here is my aws.yml:
development:
access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
production:
access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
Documentation from the GitHub paperclip wiki
Assuming you are using dotenv-rails...
In development.rb and production.rb files:
config.paperclip_defaults = {
storage: :s3,
s3_host_name: ENV['S3_HOST_NAME'],
bucket: ENV['S3_BUCKET_NAME']
}
Make sure to set these variables in your Heroku environment:
$ heroku config:set S3_HOST_NAME=your-s3-hostname-here S3_BUCKET_NAME=your-production-bucket-name-here
Paperclip-with-Amazon-S3
Documentation from rubydoc.info
This states you should have a yaml file with these details specified or write a method on your model.
rubydoc.info Paperclip::Storage::S3
No Such Key is S3's way of saying "404 Not Found."
I don't believe it has any alternate meanings.
The request was authorized and syntactically valid, but there's no file in the bucket at the specified path.
You may want to inspect the contents of your bucket from the AWS console.
I have an Active model (you can think of an Active as a User) that has authentication setup with Devise. I am trying to add a photograph attribute to my Active model and be able to upload pictures with S3.
Migration:
class AddAttachmentPhotographToActives < ActiveRecord::Migration
def self.up
change_table :actives do |t|
t.attachment :photograph
end
end
def self.down
drop_attached_file :actives, :photograph
end
end
Active model:
...
has_attached_file :photograph,
:styles => { :medium => "300x300>", :thumb => "100x100>" },
:storage => :s3,
:default_url => '/images/:attachment/missing_:style.png',
:path => "users/:id/photograph/:style.:extension",
:bucket => ... ,
:s3_credentials => {
:access_key_id => " ... ",
:secret_access_key => " ... "
}
Both config/environments/production.rb and config/environments/development.rb have the following:
config.paperclip_defaults = {
:storage => :s3,
:s3_credentials => {
:bucket => ENV[' ... '],
:access_key_id => ENV[' ... '],
:secret_access_key => ENV[' ... ']
}
}
I have my form for uploading a picture in views/devise/registrations/edit.html.erb like so: <%= f.file_field :photograph %>. However, after I select and update an Active with this form (the update goes through successfully), the path to my image (generated with <%= image_tag #active.photograph.url %>) is:
http://localhost:3000/images/photographs/missing_original.png
instead of an S3 address.
Also note I am using the following gems:
gem "paperclip", "~> 3.0"
gem 'aws-sdk'
I have never used S3 before, but after I select an image to upload and hit "enter" on my update page, my S3 bucket on the Amazon portal is still empty, so the update never went through.
Did I not set something up correctly?
Your code looks fine - are you sure you have S3 set up correctly?
Here is the code we use - hopefully this will help:
#app/models/image.rb
Class Image < ActiveRecord::Base
has_attached_file :image,
:styles => { :medium => "x300", :thumb => "x100" },
:default_url => "***********",
:storage => :s3,
:bucket => '*********',
:s3_credentials => S3_CREDENTIALS
end
#config/initializers/s3.rb
if Rails.env == "production"
# set credentials from ENV hash
S3_CREDENTIALS = { :access_key_id => ENV['S3_KEY'], :secret_access_key => ENV['S3_SECRET'], :bucket => "firststop"}
else
# get credentials from YML file
S3_CREDENTIALS = Rails.root.join("config/s3.yml")
end
#config/s3.yml
development:
access_key_id: **************
secret_access_key: ***************
bucket: *******
This works fine for us
Don't leave those keys in your code. At the minimum, place creds in config/environment.rb file:
S3_KEY='myKey'
S3_SECRET='mySecret'
#etc.
Then reference them in your code as S3_KEY, etc.
More on this and related methods: How do I store keys for API's in Rails?
I'm pretty new to ROR. I've recently deployed an app on heroku and have tried to add an attachment function to the app via paperclip.
I've followed all the steps in adding aws-s3 to my app. Here was my initial code:
user.rb (model)
has_attached_file :avatar,
:styles => {:small => "70x70>"},
:storage => :s3,
:s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
:path => ":attachment/:id/:style/:basename.:extension"
validates_attachment_size :avatar, :less_than => 1.megabytes
validates_attachment_content_type :avatar, :content_type => ['image/jpeg', 'image/png']
s3.yml (file is located in config folder) note: all of these buckets exist on my aws-s3
development:
bucket: my_avatar-dev
access_key_id: amazonaccesskey
secret_access_key: amazon_secret_access_key
test:
bucket: myapp_avatar-test
access_key_id: amazonaccesskey
secret_access_key: amazon_secret_access_key
production:
bucket: myapp_avatar-pro
access_key_id: amazonaccesskey
secret_access_key: amazon_secret_access_key
gemfile
gem 'aws-s3'
When running this configuration, I would get a error page 500 error when loading my app. Running Heroku logs showed the following error: AWS::S3::MissingAccessKey (You did not provide both required access keys.
So I followed some advice and defined the key and secret_key as environment variables to heroku, using the following line of code:
heroku config:add S3_KEY=amazonaccesskey S3_SECRET=amazon_secret_key
I then added an initializer to test environments and launch via key or .yml file depending on environment, code is as follows:
initializers/s3.rb
if Rails.env == "production"
# set credentials from ENV hash
S3_CREDENTIALS = { :access_key_id => ENV['S3_KEY'], :secret_access_key => ENV['S3_SECRET'], :bucket => "myapp_avatar-pro"}
else
# get credentials from YML file
S3_CREDENTIALS = Rails.root.join("config/s3.yml")
end
user.rb model was then update to the following:
has_attached_file :avatar, :storage => :s3, :s3_credentials => S3_CREDENTIALS
I then deployed to heroku and tested the app, but I still keep getting the same error (page 500) and error code: AWS::S3::MissingAccessKey (You did not provide both required access keys.
How is this possible if I have defined the variables in heroku? Is there something I am missing? Is it possible it's something with the gem? Also, I'm using HAML for styling... not sure that matters at all, but just in case it does. I'm quite lost, so any help would be greatly appreciated. Thank you so much!
Having just worked through the same problem and trawling a number of similar posts. I found that any of the possible configurations in the above answer i.e. declaring all of the hashes in the model, using the .yml or using the initializer all work fine from my dev and on heroku as long as the S3 bucket is of US Standard type> The choice is just about how DRY you want to be.
When I originally set S3 up, I used a European bucket. This gave me the spurious error message:
AWS::S3::MissingAccessKey (You did not provide both required access keys.
I note from the AWS site : http://docs.amazonwebservices.com/general/latest/gr/index.html?rande.html
that AWS uses a specific endpoint address for each region to reduce latency and am guessing (because I am a novice coder) that the US standard is either a default or coded into the AWS-S3 plugin. (Maybe someone can edit this up into a more complete answer?)
I solved this problem with this:
:s3_credentials => {
:access_key_id => 'mykey',
:secret_access_key => 'mykey'
I have data stored in the following file: guess what data it is? :)
S3_CREDENTIALS = Rails.root.join("config/s3.yml")
To confirm it's working, I fired up rails console and found S3_CREDENTIALS is a Pathname object. But I'm having trouble confirming that the data is there. How would I access the bucket data, for example?
Loading development environment (Rails 3.1.0.beta1)
>> S3_CREDENTIALS.isdir
NoMethodError: undefined method `isdir' for #<Pathname:0x10212f6f8>
from (irb):1
>> S3_CREDENTIALS.size
=> 282
>> S3_CREDENTIALS.data
NoMethodError: undefined method `data' for #<Pathname:0x10212f6f8>
from (irb):3
>> S3_CREDENTIALS[:bucket]
NoMethodError: undefined method `[]' for #<Pathname:0x10212f6f8>
from (irb):4
>>
On a related note, would this still work if I changed the file from s3.yml to s3.json?
If you are using this as s3 storage with Paperclip you want to leave it as yml. Inside your initializers (config/initializers) create a file called:
app_config.rb
AppConfig = YAML.load(File.read(Rails.root + 'config' + 'config.yml'))[Rails.env].with_indifferent_access
Your config for all your s3 stuff should be in the format:
config.yml
development:
s3:
access_id: access-id
secret_key: secret
bucket_name: your-bucket-name-for-development
staging:
s3:
access_id: access-id
secret_key: secret
bucket_name: your-bucket-name-for-staging
production:
s3:
access_id: access-id
secret_key: secret
bucket_name: your-bucket-name-for-production
At this point you should be able to go into your console and access your s3 data by just putting in:
AppConfig[:s3]
And you should get a hash back with all your data like:
{"access_id"=>"access-id", "bucket_name"=>"your-bucket-name-for-development", "secret_key"=>"secret"}
I just have the above as an example if you want to test your s3 stuff on development, but ordinarily you would just save to your local file directory on development, and use s3 for remote staging and production environments.
Accessing the bucket data is a different conversation and depends how you have your bucket data associated to your model. If for example your bucket data was associated to a Photo model like so:
photo.rb
require 'paperclip'
class Photo < ActiveRecord::Base
belongs_to :album
before_save :set_orientation
if AppConfig['s3']
has_attached_file :data,
:styles => {
:thumb => "200x200>",
:medium => "700x700>"
},
:storage => :s3,
:default_style => :medium,
:bucket => AppConfig['s3']['bucket_name'],
:s3_credentials => { :access_key_id => AppConfig['s3']['access_id'], :secret_access_key => AppConfig['s3']['secret_key'] },
:s3_headers => { 'Cache-Control' => 'max-age=315576000', 'Expires' => 10.years.from_now.httpdate },
:path => "/:class/:id/:style/:filename"
else
has_attached_file :data,
:styles => {
:thumb => "200x200>",
:medium => "700x700>"
},
:storage => :filesystem,
:default_style => :medium
end
private
def set_orientation
self.orientation = Paperclip::Geometry.from_file(self.data.to_file).horizontal? ? 'horizontal' : 'vertical'
end
end
I have my attach file name called data, as illustrated in has_attached_file :data. So to access some bucket data, I would call:
Photo.first.data(:thumb)
And that would pull the s3 url that the thumbnail photo was storing for the first Photo object that was returned. The above example is also using the 'paperclip' gem and 'aws-s3' gem.
config.gem 'aws-s3', :version => '>=0.6.2', :lib => 'aws/s3'
config.gem 'paperclip'
Hope this help you on your way.