I see by default carrierwave does not delete files linked to model.
How to do it?
Carrierwave should remove the files from S3 automatically for you. I just tested this out on a Rails 3.1 app.
You need to call
#image.destroy
not
#image.delete
Also use refresh button on aws s3 panel
I'm not familiar with carrierwave, but in general, hooking into the after_destroy is likely what you want.
class Model < ActiveRecord::Base
after_destroy :delete_linked_file
def delete_linked_file
# Delete the linked file here
end
end
Yes,
You can do it like this
def delete_image_folder
FileUtils.remove_dir(File.join(Rails.root, File.join( 'public' , file_name.store_dir)), :force => true)
end
but just remember that if you changed the Carrierwave configuration root, you should take it into account (default is public so this code will work)
Related
The documentation says about retrieve_from_store!:
Retrieves the file from the storage.
But when I call the method, rather than getting something like a file, I just get an array returned:
irb(main):008:0> uploader.retrieve_from_store!('my_file.png')
=> [:retrieve_versions_from_store!]
What exactly does the method do?
I was looking for the same thing today. Found something from Jonas Nicklas in the CarrierWave forum here...
retrieve_from_store! changes the state of the uploader, it
doesn't return anything sensible. You want to do this:
uploader.retrieve_from_store!('test.jpg')
uploader.do_whatever
The return value from retrieve_from_store! is irrelevant.
I just needed to use model.uploader.read to get to the bytes. It seems, uploader delegates to the file via Proxy: https://github.com/carrierwaveuploader/carrierwave/blob/master/lib/carrierwave/uploader/proxy.rb#L43
Carrierwave is not designed to work without model.
class DocumentUploader < CarrierWave::Uploader::Base
storage :file
def store!(*args)
super
#file
end
def filename
SecureRandom.uuid
end
end
file = uploader.store! file
puts file.path # ok
You can patch retrieve_from_store in the same way.
It will be tricky to retrieve important info for other methods, so please use another uploader if you can.
I've got a rails application with multiples uploaders.
In each uploaders I'd like a function to be executed after each file delete.
I can put a trigger and a callback in each uploader :
after :remove, :remove_dir
def remove_dir
FileUtils.remove_dir("#{Rails.root}/public/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}", :force => true)
end
But this is not really smart...
There is probably a better way to do that ????
The easiest solution would be to create a parent class for all uploader and then inherit other uploaders from it.
If you place a callback in the parent class it will be executed by all the children. Parent class should of course inherit from CarrierWave::Uploader::Base
Another solution would be to create a module and include it in all uploader classes.
I think that I've finally found a convenient solution thanks to Michal Szyndel that helped me and gave me good clues :
Each uploaders inherit from CarrierWave::Uploader::Base, so I add a callback to it through the carrierwave's initializer file(carrierwave.rb), and then I call it thanks to a trigger located in the concerned uploaders.
in carrierwave.rb :
module CarrierWave
module Uploader
class Base
def remove_dir
path = "#{Rails.root}/public/"+store_dir
FileUtils.remove_dir(path, :force => true) if Dir[path+'/*'].empty?
end
end
end
end
in concerned uploaders :
after :remove, :remove_dir
Once you've uploaded a file, is it possible to open that file before the model is saved?
I'm using Paperclip to save files in the /public folder.
class Book < ActiveRecord::Base
before_save :open_it
has_attached_file :upload
def open_it
a_file = open(upload.url) # this doesn't work before save ?
# ...
end
end
found it:
def model_method
f = open(self.upload.queued_for_write[:original].url)
end
Update:
Based on response from ecoologic, use .path instead of .url for more recent versions of the Paperclip gem
Close enough, but .url didn't work for me, path worked.
file = open(uploaded.queued_for_write[:original].path)
did the trick.
P.S.: Don't forget to close it in an ensure block.
I am using carrierwave and mongoid on a rails 3 application and am having an issue with an after_save callback. Consider the following
class Video
include Mongoid::Document
field :name
mount_uploader :file, VideoUploader
after_create :enqueue_for_encoding
protected
def enqueue_for_encoding
// point your encoding service to where it expects the permanent file to reside
// in my case on s3
end
end
My issue is that in my enqueue_for_encoding method, file.url points to the local tmp directory not the s3 directory.
How do I get my enqueue_for_encoding method to be called when file.url points to s3?
Thanks!
Jonathan
Check out carrierwave's howto page on Callbacks
https://github.com/jnicklas/carrierwave/wiki/How-to%3A-use-callbacks
It worked for me
Okay, I figured it out. To took a bit of hacking. So currently carrierwave does not expose an after_create hook, all of it persisting and processing happens in the after_save callback. Here is the code I used to work around it:
# Video.rb
mount_uploader :file, VideoUploader
# overwrite the file setting to flag the model that we are creating rather than saving
def file=(obj)
#new_file = true
super(obj)
end
# chain the store_file! method to enqueue_for_encoding after storing the file AND
# if the file is new
alias_method :orig_store_file!, :store_file!
def store_file!
orig_store_file!
if #new_file #means dirty
#new_file = false
enqueue_for_encoding
end
true
end
UPDATE
Woops -- that didn't work. It almost did -- the url is correct, but it is being fired permanently. Meaning the file is still in process of being loaded, and is not fully stored when enqueue_for_encoding is called
It is possible to set your enqueue_for_encoding callback on the uploader itself. But I prefer to do it this way:
class Video
# mount the uploader first:
mount_uploader :file, VideoUploader
# then add the callback:
after_save :enqueue_for_encoding, on: :create
end
You could try removing your after_create callback in the model and add the following to your uploader:
# video_uploader.rb
process :encode
def encode
model.enqueue_for_encoding
end
The process callbacks are called after the file is saved (I think) which should allow you to hook in once your file is up on S3.
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