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.
Related
I see this has been asked a few times over the years (eg Upload path based on a record value for Carrier wave Direct, Passing a parameter to the uploader / accessing a model's attribute from within the uploader / letting the user pick the thumbnail size), but I'm convinced I must be overcomplicating this, as it seems like a very simple problem...
I have a very straightforward Video model that mounts an uploader:
class Video < ApplicationRecord
mount_uploader :file, VideoUploader
end
In the controller, I allow two parameters:
def video_params
params.require(:video).permit(:title, :file)
end
In the actual VideoUploader, I seem to have access to a number of variables derived from the :file column using class builtins (eg original_filename), and I can process the file using ffmpeg parameters. However, I want the parameters to be conditional based on the :title string, and I have no idea how to scope it or access it. What is the absolute simplest way to make sure this variable is accessible to those methods?
Edit: here's the uploader code:
class VideoUploader < CarrierWave::Uploader::Base
require 'streamio-ffmpeg'
include CarrierWave::Video
case #title # not working
when "tblend_glitch"
process encode_video: [:mp4,
resolution: "1280x960",
custom: %w(-to 5 -vf scale=-2:720,tblend=all_mode=difference,tblend=all_mode=difference,tblend=all_mode=difference,spp=4:10,tblend=all_mode=average,tblend=all_mode=difference,tblend=all_mode=difference,tblend=all_mode=difference,spp=4:10,tblend=all_mode=average,tblend=all_mode=difference,tblend=all_mode=difference,tblend=all_mode=difference,spp=4:10,tblend=all_mode=average,tblend=all_mode=difference,tblend=all_mode=difference,tblend=all_mode=difference)]
...
end
def full_filename(for_file)
super.chomp(File.extname(super)) + '.mp4'
end
def filename
original_filename.chomp(File.extname(original_filename)) + '.mp4'
end
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
Thanks!
You should be able to access the instance in the uploader using the model method.
You haven't defined #title — it's nil. You can create a conditional version with the following code.
class VideoUploader < CarrierWave::Uploader::Base
version :tblend, if: :tblend_glitch? do # use a better version name
process encode_video: [:mp4,
resolution: "1280x960",
custom: %w(-to 5 -vf scale=-2:720,tblend=all_mode=difference,tblend=all_mode=difference,tblend=all_mode=difference,spp=4:10,tblend=all_mode=average,tblend=all_mode=difference,tblend=all_mode=difference,tblend=all_mode=difference,spp=4:10,tblend=all_mode=average,tblend=all_mode=difference,tblend=all_mode=difference,tblend=all_mode=difference,spp=4:10,tblend=all_mode=average,tblend=all_mode=difference,tblend=all_mode=difference,tblend=all_mode=difference)]
end
# rest of the code
private
def tblend_glitch?
model.title == 'tblend_glitch'
end
end
Ref: https://github.com/carrierwaveuploader/carrierwave#conditional-versions
In theory you can access the model and its attributes from the uploader. However, it looks like the mounted uploader gets invoked before the other attributes are assigned.
For me it worked to create the model with the regular parameters first, and assign the attribute that has the uploader mounted (in your case :file) in a second step. Then I could read all model attributes correctly from within the uploader.
In your case that would be something like this in the controller:
#video = Video.new(video_params.except(:file))
#video.file = video_params[:file] # Invoke uploader last to access the other attributes
I have an external service that creates files and stores them into S3 (that my Rails app has access too).
I want to be able to use Paperclip in order to store that S3 object, as well as a thumbnail of it. I haven't been able to find documentation on how to use Paperclip with files that are already on S3. So, my code should look like:
page = Page.find(3)
page.s3_url = s3_url # I have the s3 route. And I would need to generate a thumbnail too.
page.save
So, in other words:
How can I tell PaperClip that my attachment is already in S3?
EDIT:
Here is what I have so far:
I know the file_name, the content_length and the content_type of the file that is already uploaded in S3. So, since the association is a Page has_attached_file :screenshot
This is what I do:
#page = Page.find(3)
#page.update_attributes(screenshot_content_type: screenshot_content_type, screenshot_file_size: screenshot_file_size, screenshot_update_at: screenshot_updated_at, screenshot_file_name: screenshot_file_name)
So, now I can do:
#page.screenshot and I see the paperclip object. However, when I do:
#page.screenshot.url => The url is not the one that I originally stored the image.
I've managed to solve this problem with paperclip interpolations:
Define in your model a field, witch would store path to the uploaded files
Load the paperclip attachment info to DB through property update
With interpolations specify a paperclip attachment :path, so it first looks for uploaded file, and then for default
Profit!
This will do the trick, because of how S3 storage composes urls:
path: This is the key under the bucket in which the file will be stored. The URL will be constructed from the bucket and the path. This is what you will want to interpolate. Keys should be unique, like filenames, and despite the fact that S3 (strictly speaking) does not support directories, you can still use a / to separate parts of your file name.
Here are more details with code:
Model
class MyModel
has_attached_file :file, path: ":existent_file_or_default", storage: :s3
end
Paperclip interpolations
Put this under config/initializers
Paperclip.interpolates :existent_file_or_default do |attachment, style|
attachment.instance.existent_file_path ||
attachment.interpolator.interpolate(":class/:attachment/:id_partition/:style/:filename", attachment, style)
end
Attach existent items
MyModel.create({
existent_file_path: "http://your-aws-region.amazonaws.com/your-bucket/path/to/existent/file.jpeg",
file_file_name: "some_pretty_file_name.jpeg",
file_content_type: "image/jpeg",
file_file_size: 123456
})
S3
Paperclip S3 integration is actually relatively simple - you'll be best looking at this Railscast on how to use Paperclip, and the afore-linked documentation to give you an idea on how Paperclip works; then you can just connect it to S3
--
In short, Paperclip basically takes a file object & saves the relevant data to your db. The storage of the file is dependent on the service you associate with Paperclip
What I'm trying to say is the two elements (data allocation & file storage) are two different elements of the gem, and if you can get Paperclip to handle the inbound files, you're half-way there:
Paperclip
Default implementation:
#app/models/page.rb
Class Page < ActiveRecord::Base
has_attached_file :photo
end
This will allow you to save an "attachment" to your Page database:
#app/controllers/pages_controller.rb
Class PagesController < ApplicationController
def new
#page = Page.new
end
def create
#page = Page.new(page_params)
end
private
def page_params
params.require(:page).permit(:title, :other, :information, :photo)
end
end
#app/views/pages/new.html.erb
<%= form_for #page do |f| %>
<%= f.file_field :photo %>
<%= f.submit %>
<% end %>
This will handle Paperclip uploads directly to your own app (storing the files in the public/system directory)
In order to get it to work with S3, you just have to add the S3 credentials to the model (taken from the Paperclip documentation):
#app/models/page.rb
Class Page < ActiveRecord::Base
has_attached_file :photo,
:storage => :s3,
:s3_credentials => Proc.new{|a| a.instance.s3_credentials }
def s3_credentials
{:bucket => "xxx", :access_key_id => "xxx", :secret_access_key => "xxx"}
end
end
Path
If you'd like to call the "S3" filepath for your file - you'll have to do this:
#app/controllers/pages_controller.rb
Class PagesController < ApplicationController
def show
#page = Page.find 3
#page.photo.url #-> yields S3 path ;)
end
end
I'm searching for quite some time now a handy Method or even better, a Gem, that would allow me to CRUD Files.
Specific needs are:
Create a file from a :text string. (and control file type. e.g. test.js)
Edit the File (Rewrite) if an update has occurred to :text
Basically the whole CRUD Powerhouse.
Now, of course i know that there is no Magic Willie that will do this all by himself, i'm just looking for a right direction, from where i can start.
Use Case:
We have a documents controller with the variables:
:title
:body
:user_id
The :body (a simple text_area input) contains some .js code. How can i create a file (e.g test.js) based on :body which was entered by the User.
E.g. for simply creating the file => File.open('text.js', 'w') { |file| file.write(#something.body) }
So far i'm here
def create
#script = current_user.scripts.build(script_params)
# Creating Folder (if it doesn't exist already)
directory_name = "#{Rails.root}/public/userscripts/#{#script.user_id}"
Dir.mkdir(directory_name) unless File.exists?(directory_name)
# Write the file with model attribute :source
File.open("public/userscripts/#{#script.user_id}/#{#script.name}.user.js", 'w') { |file| file.write(#script.source) }
end
Paperclip will allow you to attach files and you can update them as well.
You can create a temporary file, assign it to the attachment attribute, then save the model.
# create and write a file with the contents you want
open "/tmp/tmpfile" do |f|
model.attachment = f
model.save!
end
You can put this in a before_save filter to always update the file attachment.
class Model < ActiveRecord::Base
has_attached_file attachment
before_save :update_attachment
def update_attachment
File.open("/tmp/tmpfile", 'w') { |file| file.write(self.body) }
open "/tmp/tmpfile" do |f|
self.attachment = f
self.attachment_content_type = "text/javascript"
end
end
i am trying to upload a file with carrierwave in my rails app and currently this is my code:
Controller:
def fileSave
#code.store!(code)
end
View:
= form_for #code = Code.new(params[:code]), :as => :code, :html => {:multipart => true} do |f|
div class="browse"
span
= f.file_field :code
= f.submit 'Upload'
Uploader:
# encoding: utf-8
class CodeUploader < CarrierWave::Uploader::Base
def pre_limit file
#require 'debugger'; debugger
if file && file.size > 100.megabytes
raise Exception.new('too large')
end
true
end
storage :file
def store_dir
"public/uploads"
end
def extension_white_list
%w(txt js ttf html)
end
def filename
"file.txt" if original_filename
end
end
Model:
require 'carrierwave/orm/activerecord'
class Code < ActiveRecord::Base
attr_accessor :code
mount_uploader :code, CodeUploader
end
And my problem is i can not store the uploaded file. x[ I am sure this is like 3 lines of code but i can not figure it out. Also the file to be uploaded is expected to be txt (probably figured that out looking the extension list).
Thanks to all readers and answerers. :}
P.S. I was wondering if i could create some kind of imaginary file, a file which is not really created. The is idea is if a take a text from a textarea and create a file (the imaginary one), store the text inside and then eventually save the whole file (maybe use carrierwave as and manually store it).
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