File uploading with Dragonfly, impossible to access images after upload (Rails 3) - ruby-on-rails

I'm trying to make a multiple drag and drop upload file system with Rails 3 and Dragonfly (or anything that would work actually)
I'm at the point where my file comes in my controller through the params hash and I can retrieve it as an ActionDispatch::Http::UploadedFile so I thought all I would have to do then is push it in my model's attribute image but it doesn't work
This my Picture model :
class Picture < ActiveRecord::Base
image_accessor :image
attr_accessible :image_name, :image_uid, :title
end
I thought this would work in my controller :
def createImage
#new_picture = Picture.new
#new_picture.image = params[:pic]
if #new_picture.save
render :json => { :picture => #new_picture }
end
end
Ok, so this registers the record with image_name nil oddly, but with the image_uid set
However, when I try to access my image <%= image_tag #picture.image.url %> I get a not found error
For example :
Request URL:http://localhost:3000/media/BAhbBlsHOgZmSSIhMjAxMi8wOS8yMi8xOV8zMF8yOF83MzBfZmlsZQY6BkVU
Request Method:GET
Status Code:404 Not Found
I'm using ruby 1.9.3 and rails 3.2.8
Any ideas ? :D

Dragonfly needs to load it's rack adapter before it is able to take encoded url requests (like the one above). The best way to load it is to add this before config.encoding = "utf-8 in your /config/application.rb
config.middleware.insert 0, 'Rack::Cache', {:verbose => true,:metastore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/meta"),:entitystore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/body")} unless Rails.env.production?
config.middleware.insert_after 'Rack::Cache', 'Dragonfly::Middleware', :images
Please note that you will need the Rack-Cache gem as well.
Hope this helps!

Related

Rendering Paperclip URL's using JBUILDER

I am building an API with Ruby on Rails.
I have Users that have avatars implemented using paperclip.
I am trying to access my paperclip URL's in my JSON output and it just crashed my heroku instance. This works perfectly locally.
Snippet of My User Model
class User < ActiveRecord::Base
has_attached_file :avatar, styles: { large: "600x600#", medium: "300x300#", thumb: "100x100#" }, default_url: "https://s3.amazonaws.com/whiztutor-marketing/photos/Profile300.png"
validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\Z/
end
Snippet of My Jbuilder for my user
json.cache! #user do
json.id #user.id
json.type #user.type
json.f_name #user.f_name
json.about #user.about
json.email #user.email
json.avatar_url #user.avatar.url(:medium)
json.referral_code #user.referral_code
json.education_level #user.education_level
json.photo_url #user.avatar.to_json
json.education_details #user.education_details.all
json.all_subjects #user.subjects.all
json.all_times #user.all_available_times.each do |time|
json.day time.schedule.to_s
json.time_block time.time_as_string
json.next_occurrence time.schedule.next_occurrence(Time.now)
end
end
I tried wrapping this into a method as found on this question and it breaks the server the exact same way. I can even run console and access these URL's directly with the server. Something with JBUILDER and PAPERCLIP just don't mix and I can't seem to get to the bottom of it. Any help is greatly appreciated.
Finally diagnosed this issue after several hours of research.
The issue is with the "json.cache! #user do" - Specifically the .cache! - I wanted to save a few server cycles and speed things up so this is how I implemented things at first.
Regardless, when I updated my JBUILDER code to the following I am no longer getting 500 server errors.
Snippet of My working Jbuilder for my user
json.id #user.id
json.type #user.type
json.f_name #user.f_name
json.about #user.about
json.email #user.email
json.small_photo #user.profile_url_small
json.medium_photo #user.profile_url_medium
json.referral_code #user.referral_code
json.education_level #user.education_level
json.education_details #user.education_details.all
json.all_subjects #user.subjects.all
json.all_times #user.all_available_times.each do |time|
json.day time.schedule.to_s
json.time_block time.time_as_string
json.next_occurrence time.schedule.next_occurrence(Time.now)
end
It's hard to say what the problem might be without knowing the error, but these things look suspicious or just wrong:
json.avatar_url #user.avatar.url(:medium)
json.photo_url #user.avatar.to_json
The second one is going to give you extra quotes you probably don't want. And why have both?
Also here:
json.all_times #user.all_available_times.each do |time|
json.day time.schedule.to_s
json.time_block time.time_as_string
json.next_occurrence time.schedule.next_occurrence(Time.now)
end
I think you want this:
json.all_times #user.all_available_times do |time|
json.day time.schedule.to_s
json.time_block time.time_as_string
json.next_occurrence time.schedule.next_occurrence(Time.now)
end
You can try this to get the url in json response, it works for me placed this method in your model.
def avatar_url
avatar.url(:medium)
end
And call this method from controller,by overriding to_json()
render :json => #friends.to_json(:methods => [:avatar_url])

Rails 4 Paperclip Images not uploading/showing (only missing.png)

First of all, these are my environment versions:
Rails: 4.1.0
Ruby: 2.1.1p76
Paperclip: 4.1
I created a scaffold (rails g scaffold Entry description:text) and further added Paperclip to the then existing model (rails g paperclip entry image).
Afterwards I migrated and everything worked fine so far.
Now when I upload an image it just doesn't display, instead "/images/original/missing.png" get's shown and there's no record of the image I just uploaded at all.
This is my model (models/entry.rb):
class Entry < ActiveRecord::Base
has_attached_file :image,
:path => ":rails_root/public/images/:class/:attachement/:id/:basename.:extension",
:url => "/images/:class/:attachement/:id/:basename.:extension"
end
My view (show.html.slim):
p#notice = notice
p
strong Description:
= #entry.description
= image_tag #entry.image.url
= link_to 'Edit', edit_entry_path(#entry)
'|
= link_to 'Back', entries_path
I have ImageMagick installed and even set the Paperclip.options within my development.rb.
I have no idea what I am missing here, it just doesn't seem to upload any images whatsoever, nor throw out any error messages.
After a little more research and a few cold drinks I found the solution!
It's necessary to either explicitly allow certain formats to get uploaded OR remove the verification check (I recommend this for development).
Doing this is as simple as adding the following line to your model (for me, entry.rb)
(SOURCE: https://stackoverflow.com/a/21898204/3686898)
do_not_validate_attachment_file_type :image
Also, I added another check in my controller (same as attr_accessible in earlier Rails versions):
private
# Use callbacks to share common setup or constraints between actions.
def set_entry
#entry = Entry.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def entry_params
params.require(:entry).permit(:description, :image)
end
Alrighty, I hope this helps someone out :)
(Always remember to have a look at your server-logs. It provides golden information!)
Check your
:path => ":rails_root/public/images/:class/:attachement/:id/:basename.:extension"
make sure that is tracing back to where the image is stored

save_and_process post processing 403 Forbidden Carrierwave_direct S3 Fog

I'm trying to develop direct fileuploads to S3 for my app. I'm following the github tutorial for this and everything is more or less ok but get an error message when trying to make the post processing.
I did the following:
I have an activerecord model called clip.rb:
class Clip < ActiveRecord::Base
belongs_to :attachable, :polymorphic => true
mount_uploader :avatar, AvatarUploader
attr_accessible :id, :avatar, :name, :clipat_file_name, :attachable_id, :attachable_type, :clipat, :project_id, :user_id, :path, :parent_id,
def save_and_process_avatar(options = {})
if options[:now] or 1==1
self.remote_avatar_url = avatar.direct_fog_url(:with_path => true)
save
else
Resque.enqueue(AvatarProcessor, attributes)
end
end
Then I have an uploader: avatar_uploader.rb
class AvatarUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
include CarrierWaveDirect::Uploader
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}" #I removed /#{model.id} from the template because it creates an empty directory on the server. But If I put it back, the same problem remains
end
version :thumb do
process :resize_to_limit => [50, 50]
end
end
and an avatar controller:
class AvatarsController < ApplicationController
def new
#uploader = Clip.new.avatar
#uploader.success_action_redirect = 'http://localhost:3000/clips'
end
end
and finally my clip_controller:
class ClipsController < ApplicationController
def index
if params[:key]
key=params[:key].split("/")
clip = Clip.new
clip.attachable_id = key[3]
clip.attachable_type = "Pmdocument"
clip.key = params[:key]
# clip.save
clip.save_and_process_avatar
end
#clips = Clip.where("avatar is not null")
respond_to do |format|
format.html # index.html.erb
format.json { render json: #clips.collect { |p| p.to_jq_upload }.to_json }
end
end
When I upload a file, if I just save my "clip", everything is ok. If I use the save_and_process method however, an error arises at line:
self.remote_avatar_url = avatar.direct_fog_url(:with_path => true)
This is the error message:
OpenURI::HTTPError (403 Forbidden):
app/models/clip.rb:38:in `save_and_process_avatar'
app/controllers/clips_controller.rb:22:in `index'
Rendered /Users/nico/.rvm/gems/ruby-1.9.2-p290/gems/actionpack-3.1.3/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.4ms)
Rendered /Users/nico/.rvm/gems/ruby-1.9.2-p290/gems/actionpack-3.1.3/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (1.2ms)
Rendered /Users/nico/.rvm/gems/ruby-1.9.2-p290/gems/actionpack-3.1.3/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (5.2ms)
I've been hanging on this for two days so, any help would be greatly appreciated!!! Thanks!!! Nicolas.
My bet is that the URL supplied to self.remote_avatar_url is incorrect. I had this same problem and the code that CWDirect gem provides didn't work for me and gave me an incorrect URL, thus CarrierWave couldn't download and process the image. The whole 403 Forbidden error message was a crap message from Amazon--this leads one to believe that there is something wrong with Permissions; in my case there was absolutely nothing wrong with permissions. It was just that there was no image there. Here is the code that got this working for me, notice I've changed how the URL is formed:
def save_and_process_image(options = {})
if options[:now]
# debugger
self.remote_image_url = image.direct_fog_url+self.key # OLD CODE THAT AINT WORKIN! --> image.direct_fog_url(:with_path => true)
save
else
# Resque.enqueue(AvatarProcessor, attributes)
# TODO: Implement background processing
end
end
Note that the name of my mounted field is image and not avatar.
How I got to this point and fixed it--try this out, use the rails debugger (just uncomment the debugger line above) to freeze the program just before the self.remote_image_url line, then while in debug mode type irb to start up the console. Then you can print out and see really what value 'image.direct_fog_url(:with_path => true)' is giving you. You can copy and paste this into a browser. If it's wrong (probably is) then you will get the silly Permissions error (even though it's not a permissions problem), but when it's correct either you will see the uploaded image or the image will download.
It's a good idea to have your Amazon S3 console open and viewing your dev bucket so you can find the image that just uploaded. Find the image in the console and go to its properties and you can see the web address/url that you are supposed to be using.
Hope this helps. Because of the misleading error this was hard to track down for me, I spent a bunch of time trying to correct permissions on my S3 bucket but this wasn't the problem, just that code from the CWDirect github page doesn't work for me (gem version??).

How can I reference images in the asset pipeline from a model?

I have a model with a method to return a url to a person's avatar that looks like this:
def avatar_url
if self.avatar?
self.avatar.url # This uses paperclip
else
"/images/avatars/none.png"
end
end
I'm in the midst of upgrading to 3.1, so now the hard-coded none image needs be referenced through the asset pipeline. In a controller or view, I would just wrap it in image_path(), but I don't have that option in the model. How can I generate the correct url to the image?
I struggled with getting this right for a while so I thought I'd post the answer here. Whilst the above works for a standard default image (i.e. same one for each paperclip style), if you need multiple default styles you need a different approach.
If you want to have the default url play nice with the asset pipeline and asset sync and want different default images per style then you need to generate the asset path without fingerprints otherwise you'll get lots of AssetNotPrecompiled errors.
Like so:
:default_url => ActionController::Base.helpers.asset_path("/missing/:style.png", :digest => false)
or in your paperclip options:
:default_url => lambda { |a| "#{a.instance.create_default_url}" }
and then an instance method in the model that has the paperclip attachment:
def create_default_url
ActionController::Base.helpers.asset_path("/missing/:style.png", :digest => false)
end
In this case you can still use the interpolation (:style) but will have to turn off the asset fingerprinting/digest.
This all seems to work fine as long as you are syncing assets without the digest as well as those with the digest.
Personally, I don't think you should really be putting this default in a model, since it's a view detail. In your (haml) view:
= image_tag(#image.avatar_url || 'none.png')
Or, create your own helper and use it like so:
= avatar_or_default(#image)
When things like this are hard in rails, it's often a sign that it's not exactly right.
We solved this problem using draper: https://github.com/jcasimir/draper. Draper let us add a wrapper around our models (for use in views) that have access to helpers.
Paperclip has an option to specify default url
has_attached_file :avatar, :default_url => '/images/.../missing_:style.png'
You can use this to serve default image' in case user has not uploaded avatar.
Using rails active storage I solved this problem by doing this:
# Post.rb
def Post < ApplicationRecord
has_one_attached :image
def thumbnail
self.image.attached? ? self.image.variant(resize: "150x150").processed.service_url : 'placeholder.png';
end
def medium
self.image.attached? ? self.image.variant(resize: "300x300").processed.service_url : 'placeholder.png';
end
def large
self.image.attached? ? self.image.variant(resize: "600x600").processed.service_url : 'placeholder.png';
end
end
Then in your views simply call:
<%= image_tag #post.thumbnail %>,

How do I download a remote image from another site to a file_column in Ruby on Rails?

first question, hopefully I don't mess it up :)
A bit of a Ruby on Rails newbie (also Ruby newbie) and have stumbled upon a problem with the intended behavior of the application.
I have a file_column :image in model picture that belongs to model product, which can have many pictures.
The file_column works just fine when used as I think it's meant to be used and that's for uploading image using <%= file_column_field "picture", "image" %> etc. That part works just fine.
The problem comes with the intention of having a text field where user can enter a css -selector for an image tag on their site (they've registered the site and the path to the page where the image should be). I haven't been able to figure out how to properly download the image from that other site "under the hood".
Using these two methods both result in Do not know how to handle a string with value 'GIF89ad..... followed by loads of "binary".
Method 1:
url = URI.parse(picture_www.external_url)
Net::HTTP.start(url.host, url.port) {|http|
resp = http.get(url.path)
picture_www.image = resp.body unless resp.nil?
}
Method 2:
res = open(picture_www.external_url)
picture_www.image = res.read unless res.nil?
The external_url contains the correct url and the download goes ok, so the problem seems to be in the way I'm trying to assign the image to the file_column field. Naturally the problem could be the way I'm downloading the image, I have no idea TBH where the problem actually lies... :)
Anyone able to help me please?
Update:
Trying to use a tempfile "causes undefined method 'original_filename' for" etc
Net::HTTP.start(url.host, url.port) {|http|
resp = http.get(url.path)
tempfile = Tempfile.new('test.jpg')
File.open(tempfile.path, 'wb') do |f|
f.write resp.body
end
picture_www.image = tempfile unless resp.nil?
}
Update2:
Debugging shows me that an uploaded file has attributes #content_type ("image/jpeg" for instance) and #original_path (file name without path) under #_dc_obj and #tmpfile when the tempfile I created does not. Setting these properly would perhaps make this work? How do I set those properly? And if setting those values properly, would the file downloading be done "properly"? After ofcourse re-structuring the code once I get a working solution.
Update3:
From Minver's answer I got the solution for "original_filename" issue and this code seems to work:
io = open(picture_www.external_url)
def io.original_filename; base_uri.path.split('/').last; end
io.original_filename.blank? ? nil : io
picture_www.image = io
No idea though, if this is the "proper" way to do this or not, but this is what I'll be using for now unless some "clearly the right way to do it" solution appears :)
-Pkauko
The UrlUpload method by Joe Martinez is a good solution but the code is missing a key method. If you over-ride the method_missing, you should always also over-ride the respond_to? method as well. In this case it is especially important since some software uses respond_to? when deciding whether to do a multipart-post.
For example, the Faraday gem does this:
def has_multipart?(body)
body.values.each do |v|
if v.respond_to?(:content_type)
return true
elsif v.respond_to?(:values)
return true if has_multipart?(v)
end
end
false
end
So, if you are going to use the UrlUpload code above, I suggest you add the following method:
def respond_to?(symbol)
attachment_data.respond_to?(symbol) || super
end
Then Faraday and other related gems will be able to use an instance of this class to generate a proper multipart-post.
I don't know but maybe this is what you are looking for. When you save the image you provide a css_selector and gets a image file in return.
This is the view:
<%= form_for(#image) do |f| %>
<div class="field">
<%= f.label :css_selector %><br />
<%= f.text_field :css_selector %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
and this is the model:
class Picture < ActiveRecord::Base
require 'open-uri' # Required to download the photo
require 'mechanize' # Good gem to parse html pages
belongs_to :product
# Define the css_selector (not required as a filed in the database)
attr_accessor :css_selector
# Before we save the image, we download the photo if image has a css_selector value
before_save :download_remote_photo, :if => :css_selector_provided?
private
# Check if the attribute is provided
def css_selector_provided?
!self.css_selector.blank?
end
# This method opens the page where the photo is
# and grab the url to the image using a css-selector
def fetch_photo_url
agent = Mechanize::new
page = agent.get(HERE_IS_THE_URL_TO_THE_PAGE_YOU_WANNA_SCRAPE)
doc = Nokogiri::HTML(page.body)
image_element = doc.at_css(self.css_selector) # Get the image on that page using the css selector
image_url = image_element[:src]
end
def download_remote_photo
self.image = do_download_remote_photo(fetch_photo_url)
end
def do_download_remote_photo(photo_url)
io = open(URI.parse(URI.escape(photo_url)))
def io.original_filename; base_uri.path.split('/').last; end
io.original_filename.blank? ? nil : io
rescue # catch url errors with validations instead of exceptions (Errno::ENOENT, OpenURI::HTTPError, etc...)
end
end
Haven't tested the code but I hope you get the idea!
Here ya go
require 'open-uri'
class UrlUpload
EXTENSIONS = {
"image/jpeg" => ["jpg", "jpeg", "jpe"],
"image/gif" => ["gif"],
"image/png" => ["png"]
}
attr_reader :original_filename, :attachment_data
def initialize(url)
#attachment_data = open(url)
#original_filename = determine_filename
end
# Pass things like size, content_type, path on to the downloaded file
def method_missing(symbol, *args)
if self.attachment_data.respond_to? symbol
self.attachment_data.send symbol, *args
else
super
end
end
private
def determine_filename
# Grab the path - even though it could be a script and not an actual file
path = self.attachment_data.base_uri.path
# Get the filename from the path, make it lowercase to handle those
# crazy Win32 servers with all-caps extensions
filename = File.basename(path).downcase
# If the file extension doesn't match the content type, add it to the end, changing any existing .'s to _
filename = [filename.gsub(/\./, "_"), EXTENSIONS[self.content_type].first].join(".") unless EXTENSIONS[self.content_type].any? {|ext| filename.ends_with?("." + ext) }
# Return the result
filename
end
end
# Make it always write to tempfiles, never StringIO
OpenURI::Buffer.module_eval {
remove_const :StringMax
const_set :StringMax, 0
}

Resources