Rails, before_create - ruby-on-rails

Im rails beginner and I tried to add an carrierwave upload for image urls like shown in the episode #253 from railscast. So far all works good when i insert an normal link, for example
http:/.....g/dog-01.jpg
My problem is that I want to retrieve my images from a javascript plugin that gives a data:image out, how you can see here:
var url = canvas.toDataURL('image/png');
document.getElementById("canvascontent").value = url;
Also you can see that I insert the data:image to an input(#canvascontent).
<%= ff.text_field :remote_name_url, class => "canvascontent" %>
So now my problem, is that when I try to save simply the data:image from the text_field :remote_name_url, this dont works because it is encoded with base64.
The good thing is that Ruby has a Base64 encoder. To encode the data:image and save it in an image file (here png) I wrote a simply code, that I can execute from my console:
require 'base64'
data_url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAB....."
png = Base64.decode64(data_url['data:image/png;base64,'.length .. -1])
File.open('testal', 'wb') { |f| f.write(png) }
So to come to the end. My problem is that I don't know how to achieve this with rails!
I know that there is an "code" for the model before_create, i tried something like that, but I didn't worked. I think because i have to safe the image first temporally!
before_create do
require 'base64'
self.remote_name_url = Base64.decode64(remote_name_url['data:image/png;base64,'.length .. -1])
end
class Painting < ActiveRecord::Base
belongs_to :treatment
attr_accessible :name, :image, :remote_name_url
mount_uploader :name, BildUploader
before_create do
require 'base64'
self.remote_name_url = Base64.decode64(remote_name_url['data:image/png;base64,'.length .. -1])
end
end
<
class TreatmentsController < ApplicationController
def create
#patient = Patient.find(params[:patient_id])
#treatment = #patient.treatments.create(params[:treatment])
redirect_to patient_path(#patient)
end
def destroy
#patient = Patient.find(params[:patient_id])
#treatment = #patient.treatments.find(params[:id])
#treatment.destroy
redirect_to patient_path(#patient)
end
end

This article from the Carrierwave wiki seems promising:
https://github.com/carrierwaveuploader/carrierwave/wiki/How-to%3A-Upload-from-a-string-in-Rails-3
There's also the older technique of using Rack::Test::UploadedFile and a StringIO object, but that looks weirder in code that's not intended for the test environment.

Related

Displaying an Image from Rails Seeds.rb [duplicate]

Please suggest me a way to save an image from an URL by Paperclip.
In Paperclip 3.1.4 it's become even simpler.
def picture_from_url(url)
self.picture = URI.parse(url)
end
This is slightly better than open(url). Because with open(url) you're going to get "stringio.txt" as the filename. With the above you're going to get a proper name of the file based on the URL. i.e.
self.picture = URI.parse("http://something.com/blah/avatar.png")
self.picture_file_name # => "avatar.png"
self.picture_content_type # => "image/png"
Here is a simple way:
require "open-uri"
class User < ActiveRecord::Base
has_attached_file :picture
def picture_from_url(url)
self.picture = open(url)
end
end
Then simply :
user.picture_from_url "http://www.google.com/images/logos/ps_logo2.png"
It didn't work for me until I used "open" for parsed URI.
once I added "open" it worked!
def picture_from_url(url)
self.picture = URI.parse(url).open
end
My paperclip version is 4.2.1
Before open it wouldn't detect the content type right, because it wasn't a file. It would say image_content_type: "binary/octet-stream", and even if I override it with the right content type it wouldn't work.
First download the image with the curb gem to a TempFile and then simply assign the tempfile object and save your model.
Into official documentation is reported here https://github.com/thoughtbot/paperclip/wiki/Attachment-downloaded-from-a-URL
Anyway it seems not updated, because in last version of paperclip something has changed and this line of code is no more valid:
user.picture = URI.parse(url)
It raise an error, in particular this error is raised:
Paperclip::AdapterRegistry::NoHandlerError: No handler found for #<URI:: ...
The new correct syntax is this one:
url = "https://www.example.com/photo.jpeg"
user.picture = Paperclip.io_adapters.for(URI.parse(url).to_s, { hash_digest: Digest::MD5 })
Also we need to add these lines into config/initializers/paperclip.rb file:
Paperclip::DataUriAdapter.register
Paperclip::HttpUrlProxyAdapter.register
Tested this with paperclip version 5.3.0 and it works.
It may helpful to you. Here is the code using paperclip and image present in remote URL .
require 'rubygems'
require 'open-uri'
require 'paperclip'
model.update_attribute(:photo,open(website_vehicle.image_url))
In model
class Model < ActiveRecord::Base
has_attached_file :photo, :styles => { :small => "150x150>", :thumb => "75x75>" }
end
As those are old Answer's here's a newer one:
Add Image Remote URL to your desired Controller in the Database
$ rails generate migration AddImageRemoteUrlToYour_Controller image_remote_url:string
$ rake db:migrate
Edit your Model
attr_accessible :description, :image, :image_remote_url
.
.
.
def image_remote_url=(url_value)
self.image = URI.parse(url_value) unless url_value.blank?
super
end
*In Rails4 you have to add the attr_accessible in the Controller.
Update your form, if you allow other to upload an Image from a URL
<%= f.input :image_remote_url, label: "Enter a URL" %>
This is a hardcore method:
original_url = url.gsub(/\?.*$/, '')
filename = original_url.gsub(/^.*\//, '')
extension = File.extname(filename)
temp_images = Magick::Image.from_blob open(url).read
temp_images[0].write(url = "/tmp/#{Uuid.uuid}#{extension}")
self.file = File.open(url)
where Uuid.uuid just makes some random ID.

Direct upload with Cloudinary and Carrierwave

I've been following the instructions provided by Cloudinary, but have not been able to get direct uploading working. To further complicate things my image upload is a polymorphic class and is usually in a nested form.
I'm using both the Cloudinary and Carrierwave gems. In the non-direct setup everything works properly however unicorn times out if there are too many images being uploaded at once (which may frequently be the case)
Below is the partial that adds a file upload. It is nested in multiple different forms and the user can add and remove fields dynamically. Per the instructions, I tried to replace = f.file_field :asset and = f.hidden_field :asset_cache with = cl_image_upload :asset, however, this throws an error of: wrong number of arguments (1 for 2..3). When adding a second parameter it is appended to data-cloudinary-field in the generated HTML. Additionally no upload takes place when an image is added and no reference is attached to the record.
_image_fields.html.haml
.image-field-group
.field
= f.label :asset, "Image"
= cl_image_upload :asset
/ = f.file_field :asset
/ = f.hidden_field :asset_cache
- if f.object && f.object.asset && f.object.asset.filename
.image-box
= cl_image_tag(f.object.asset.filename.to_s, :transformation => 'hint', alt: f.object.asset.filename.to_s)
.remove-fields
= link_to_remove_fields f
Here are the associated files:
image.rb
class Image < ActiveRecord::Base
default_scope order('images.id ASC')
attr_accessible :asset,
:asset_cache
belongs_to :imageable, polymorphic: true
mount_uploader :asset, ImageUploader
end
image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
include Cloudinary::CarrierWave
def extension_white_list
%w(jpg jpeg gif png)
end
end
EDIT: Added Images Controller
images_controller.rb
class ImagesController < ApplicationController
before_filter :load_imageable
def index
#images = #imageable.images
end
def new
#image = #imageable.images.new
end
def create
#image = #imageable.images.new(params[:image])
if #image.save
redirect_to #imageable, notice: "Image created."
else
render :new
end
end
private
def load_imageable
resource, id = request.path.split('/')[1, 2]
#imageable = resource.singularize.classify.constantize.find(id)
end
end
The following documentation section describes how to use direct image uploading from the browser in your Ruby on Rails application. Make sure to include jQuery and the required plugins in the correct order and to add the required Javascript configuration.
http://cloudinary.com/documentation/rails_image_upload#direct_uploading_from_the_browser
Embedding a file input field that performs direct image uploading is done using the cl_image_upload_tag helper method that accepts the name of the input field.
When using CarrierWave, you can use the cl_image_upload helper method. When calling this method directly, you need to pass the object name and the attribute them (as you do with other Rails view helper methods such as text_field_tag and text_field). Alternatively, you can use it with Rails' standard form builders.
Assuming your model is called entity and your attribute with a mounted CarrierWaver uploader is called asset, the following view code should embed a signed file input field for direct uploading from the browser:
= form_for(:entity) do |f|
= f.cl_image_upload(:asset)
In addition, as you can see below you can use present? to check if the asset exists and pass the CarrierWave attribute directly to cl_image_tag. Alternatively, you can use CarrierWave standard versions instead of dynamically building image URLs using cl_image_tag.
- if f.object && f.object.asset.present?
.image-box
= cl_image_tag(f.object.asset, :transformation => 'hint', alt: f.object.asset.filename.to_s)
If the file input field was added successfully to your view but no direct uploading is performed, you should verify that there are no Javascript errors in the console and that all jQuery plugins were included correctly.

Carrierwave : error with model.id in the store_path

I'm trying to write a method to store an image from a given url, inside a ruby worker. It comes along my Rails app in which I display the object image. Here is what I've come up with:
#message = Message.create!
my_uploader = PhotoUploader.new
photo = open(image_url)
#message[:photo] = my_uploader.store!(photo)
#message[:photo] = my_uploader.filename
#message.save!
the PhotoUploader:
def store_dir
Rails.env.production? ? (primary_folder = "production") : (primary_folder = "test")
"#{primary_folder}/media/#{model.id}"
end
the Message class:
class Message < ActiveRecord::Base
attr_accessible :content, :photo, :user_id
mount_uploader :photo, PhotoUploader
end
The model.id returns an error in the storage path. The model is nil even after saving it, and the file is stored in #{primary_folder}/media
I think it's that you're not calling store! from the model, which is why model.id doesn't work, because model is nil
This will probably do it for you:
#message = Message.create!
photo = open(image_url)
#message.photo = PhotoUploader.new
#message.photo.store!(photo)
#message.photo = #message.photo.filename
#message.save!
I tried this out in the rails console, and #message[:photo].store!(photo) gave the same error, but #message.photo.store!(photo) worked.

Displaying a Carrierwave filename in the view

I am trying to display the filename of a Carrierwave attachment in a Rails erb template. The following does not work:
<%= #page.form.filename %>
This seems in line with the documentation. Is some additional step needed?
My page model looks like this:
class Page < ActiveRecord::Base
mount_uploader :form, FormUploader
end
The form uploader looks like this:
class FormUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def extension_white_list
%w(pdf)
end
end
I have been able to get the filename via the file internal parameter:
<%= #page.form.file.filename %>
The documentation you're looking at is the sanitized file, it's what it uses for actually storing a file. The part you're looking for is FormUploader, which is an Uploader, and part of http://rubydoc.info/gems/carrierwave/0.5.2/CarrierWave/Uploader
If you want to get the file name, you could either read it from the database column directly, or use File.basename(#page.form.path) to extract it easily.
The Carrierwave docs might be a bit off, but recommended way seems to be:
#page.form.file.identifier
#adamonduty's solution is great. Another solution I used before, just create a method on the model:
def name
file.path.split("/").last
end
You're right #epylinkn. Documentation points towards using:
#page.form.file.identifier
But when I use that, I always get nil (just as #Cheng commented).
I then inspected my objects methods (#page.form.file.methods.inspect), and found the following to work:
#page.form.file_identifier
In your model's associated uploader class, define a filename method.
def filename
File.basename(path)
end
You can then call
model_instance.file.filename
Works as of CarrierWave 1.1.0. This is a succinct restatement/amalgamation of kikito and Chris Alley's responses above.
If you're using ActiveRecord, you can directly access the field named form in two ways:
def my_method
self[:form]
end
or
def my_method
form_before_type_cast
end
The second method is read-only.
CarrierWave::SanitizedFile has a private original_filename method containing the filename of the uploaded file. (docs: http://rdoc.info/github/jnicklas/carrierwave/master/CarrierWave/SanitizedFile:original_filename)
After reading through this thread from the CarrierWave mailing list, none seemed to fit my needs. With something like
class Upload < ActiveRecord::Base
mount_uploader :file, FileUploader
# ...
I heavily modify the :file column value from the original filename. Due to this I decided to track the original filename in a separate column from the one bound to CarrierWave. In my FileUploader I simply added a reader that wraps the private original_filename method:
def original_file
original_filename
end
I then added a before_create event to the Upload class (my Upload records are never modified, so a before_create is acceptable for my needs)
before_create do
self.original_file = self.file.original_file
end
I'm assuming you've got models like this?
class Page
mount_uploader :form, FormUploader
end
If so you should be able to call:
#page.form.url
#page.form.filename
Are you sure you've uploaded/attached the file correctly? What do you see when you inspect #page.form? Remember, the attachment will not be saved until you've fully processed the upload.
This is my solution:
before_save :update_file_attributes
def update_file_attributes
if file.present? && file_changed?
self.content_type = file.file.content_type
self.file_size = file.file.size
self.file_name = read_attribute(:file)
end
end

Save image from URL by paperclip

Please suggest me a way to save an image from an URL by Paperclip.
In Paperclip 3.1.4 it's become even simpler.
def picture_from_url(url)
self.picture = URI.parse(url)
end
This is slightly better than open(url). Because with open(url) you're going to get "stringio.txt" as the filename. With the above you're going to get a proper name of the file based on the URL. i.e.
self.picture = URI.parse("http://something.com/blah/avatar.png")
self.picture_file_name # => "avatar.png"
self.picture_content_type # => "image/png"
Here is a simple way:
require "open-uri"
class User < ActiveRecord::Base
has_attached_file :picture
def picture_from_url(url)
self.picture = open(url)
end
end
Then simply :
user.picture_from_url "http://www.google.com/images/logos/ps_logo2.png"
It didn't work for me until I used "open" for parsed URI.
once I added "open" it worked!
def picture_from_url(url)
self.picture = URI.parse(url).open
end
My paperclip version is 4.2.1
Before open it wouldn't detect the content type right, because it wasn't a file. It would say image_content_type: "binary/octet-stream", and even if I override it with the right content type it wouldn't work.
First download the image with the curb gem to a TempFile and then simply assign the tempfile object and save your model.
Into official documentation is reported here https://github.com/thoughtbot/paperclip/wiki/Attachment-downloaded-from-a-URL
Anyway it seems not updated, because in last version of paperclip something has changed and this line of code is no more valid:
user.picture = URI.parse(url)
It raise an error, in particular this error is raised:
Paperclip::AdapterRegistry::NoHandlerError: No handler found for #<URI:: ...
The new correct syntax is this one:
url = "https://www.example.com/photo.jpeg"
user.picture = Paperclip.io_adapters.for(URI.parse(url).to_s, { hash_digest: Digest::MD5 })
Also we need to add these lines into config/initializers/paperclip.rb file:
Paperclip::DataUriAdapter.register
Paperclip::HttpUrlProxyAdapter.register
Tested this with paperclip version 5.3.0 and it works.
It may helpful to you. Here is the code using paperclip and image present in remote URL .
require 'rubygems'
require 'open-uri'
require 'paperclip'
model.update_attribute(:photo,open(website_vehicle.image_url))
In model
class Model < ActiveRecord::Base
has_attached_file :photo, :styles => { :small => "150x150>", :thumb => "75x75>" }
end
As those are old Answer's here's a newer one:
Add Image Remote URL to your desired Controller in the Database
$ rails generate migration AddImageRemoteUrlToYour_Controller image_remote_url:string
$ rake db:migrate
Edit your Model
attr_accessible :description, :image, :image_remote_url
.
.
.
def image_remote_url=(url_value)
self.image = URI.parse(url_value) unless url_value.blank?
super
end
*In Rails4 you have to add the attr_accessible in the Controller.
Update your form, if you allow other to upload an Image from a URL
<%= f.input :image_remote_url, label: "Enter a URL" %>
This is a hardcore method:
original_url = url.gsub(/\?.*$/, '')
filename = original_url.gsub(/^.*\//, '')
extension = File.extname(filename)
temp_images = Magick::Image.from_blob open(url).read
temp_images[0].write(url = "/tmp/#{Uuid.uuid}#{extension}")
self.file = File.open(url)
where Uuid.uuid just makes some random ID.

Resources