The files uploaded succesfully using fog to s3 on carrierwave seem to be disappearing automatically.How do I prevent this from happening. The fog public setting is set to true.
For some reason, Carrierwave deletes files not only when the model is deleted, but also when it's updated, even if you don't touch the mounted uploader field. There is supposed to be a config setting remove_previously_stored_files_after_update that when set false, prevents this from happening, but I haven't had any luck setting it false – it still deletes my files on model update.
Inside your uploader class you can modify the #remove! method.
class FileUploader < CarrierWave::Uploader::Base
#...
def remove!
# do some stuff to confirm that you want the file removed,
# otherwise return. You have access to model record as 'model'
super
end
end
Related
I use Shrine in a Ruby on Rails application to create the process of resizing and uploading images to storage.
My current code is:
image_uploader.rb
require "image_processing/mini_magick"
class ImageUploader < Shrine
plugin :derivatives
Attacher.derivatives_processor do |original|
magick = ImageProcessing::MiniMagick.source(original)
{
resized: magick.resize_to_limit!(120, 120)
}
end
end
user.rb
class User < ApplicationRecord
include ImageUploader::Attachment(:image)
before_save :image_resize
def image_resize
self.image_derivatives!
end
end
I implemented it while reading the official documentation, but this is not desirable in two ways.
Requires trigger in model code. Can it be completed with only image_uploader.rb?
Access to images generated with this code requires a "resized" prefix(e.g. #user.image(:resized).url), and the original image will also remain in storage. I want to process the original image itself.
Is there a way to upload while solving these two issues?
You can add the following patch, which will trigger derivatives creation as part of promoting cached file to permanent storage:
# put this in your initializer
class Shrine::Attacher
def promote(*)
create_derivatives
super
end
end
You can just override the model method that retrieves the attached file to return the resized version. You can use the included plugin to do this for all models using this uploader:
class ImageUploader < Shrine
# ...
plugin :included do |name|
define_method(name) { super(:resized) }
end
end
As for the second question: It will still keep the original file in the storage, but just return the resized version instead by default. It's generally better to do this in a view decorator instead.
You always want to keep the original file in the storage, because you never know when you'll need to reprocess it. It can happen that you find your current resizing logic not to be ideal for certain filetypes and sizes, in which case you'll need to regenerate the resized version for previous attachments. And you wouldn't be able to do that if you didn't have the original file anymore.
I have an avatar uploader which has multiple versions. Some of them are generated synchronously on upload, others are generated conditionally on request (following this approach):
user.avatar.is_processing_delayed = true
user.avatar.recreate_versions!(:png_x60y80)
I put this code in a controller and serve the generated version from there once, after that it's served by nginx (since it's already generated). This does actually create the file and if I call user.avatar.png_x60x80.file on the same uploader instance right after recreate_versions! I get a file object. However, if I call it on another instance of the same avatar (e.g. User.find(user.id).avatar.png_x60x80.file) I get nil. This won't be a issue, but I believe that it causes the following problem: when I remove the user's avatar only versions that were created synchronously are deleted. Somehow recreare_versions! does not persist data about recreated versions. Is there something that I'm missing? I would also like to remove all versions that were created on request when avatar is updated, so that nginx wouldn't serve previously generated versions of an old avatar, but it's also problematic due to this problem.
Carrierwave version: 1.0.
Eventually I settled for the following workaround. Not perfect, but works as expected.
mount_uploader :avatar, AvatarUploader
def remove_avatar!(*args)
remove_all_avatar_versions
super(*args)
# Somehow wrapping the "remove_avatar!" method changes its behavior:
# model attribute is not updated and we have to update it manually.
write_attribute(:avatar, nil)
end
def avatar=(*args)
remove_all_avatar_versions
super(*args)
end
private
def remove_all_avatar_versions
return unless avatar?
avatar.versions.each_key do |v|
# You have to implement the avatar_path method.
path = avatar_path(v)
File.delete(path) if File.exist?(path)
end
end
I am using CarrierWave with fog to upload my images to S3.
I have model Image that can represent images of different sizes and according to that needs to be saved in different folder.
For example, for image.jpg I could have two different uploaded versions that need to be saved as:
'images/large/image.jpg'
'images/small/image.jpg'
There could be arbitrary number of use cases and versions using minimagick can't cover them all.
So far I haven't been able to find solution. Does anyone know how to do this?
I've seen this question asked a few times so I'll write what my final solution is.
Instead of defining mount_uploader on model I decided to just use Uploader independently and save urls to records later.
Dynamically changing store_dir and filename can be accomplished like this
uploader = Uploader.new
uploader.define_singleton_method(:store_dir) do
'new_store_dir'
end
uploader.define_singleton_method(:filename) do
'new_filename'
end
uploader.store!(image)
Using this approach you can also define names with local variables or whatever you have available in controller.
Hopefully it helps someone else as well.
in order to change where uploaded files are put, just override the store_dir method:, for your case (reference link)
class Image < CarrierWave::Uploader::Base
storage :file
# this if you using use condition for folder location
def store_dir
if model.somefield == "somecondition
"uploads/#{model.somefield}"
elsif model.somefield == "somecondition
"uploads/location2"
else
"uploads/default_dir"
end
end
# this is if you want to use version_name
def store_dir
'images/#{version_name}/'
end
end
I have a Document model with a file that is uploaded to S3 using Carrierwave(fog) with my uploader (mount_uploader :file, DocumentUploader). I am also using the 'paranoia' gem's acts_as_paranoid to soft delete the documents. Upon destroy I wish to move the attached file to an 'archive' folder in the same directory. Then I plan on moving it back to the original(parent) directory when a deleted document is restored.
I have the following in my model:
skip_callback :commit, :after, :remove_file!
before_destroy :move_file_to_archive
after_restore :fetch_file_from_archive
And within the method move_file_to_archive, I establish a connection to amazon using fog and do the following to move the file to archive:
bucket = connection.directories.get(bucket_name)
file = bucket.files.get(self.file.file.path)
new_path = file.key.split('/')[0..-2].join('/') + '/archive/' + file.key.split('/')[-1]
new_file = file.copy(bucket_name, new_path)
file.destroy
The problem is that I cannot find a way to get my document object to point to the new(archived) file instead of the old one. Somehow, when the object is being destroyed, I want the self.file.path to change to the archived path instead of the original path. And then revert it when the document is being restored. Any help would be appreciated!
Got it to work myself. I added a condition to my DocumentUploader to set the path to contain /archive/ in case the document had a value present in paranoia's deleted_at field. Just doing that makes carrierwave look at the archive path if the document is currently in deleted state.
I am using carrierwave to upload images. In uploaders/image_uploader.rb, I have
class ImageUploader < CarrierWave::Uploader::Base
def store_dir
'/public/uploads/images'
end
end
I would like to move the uploads directory to:
/shared/uploads/images
I obviously need to modify uploaders/image_uploader.rb to reflect the new path. In addition, I need to move the already uploaded images from:
/public/uploads/images
to:
/shared/uploads/images
My assumption is that these are the only changes I need to make, as in I don't have to make any changes to the DB or anything else. Is this correct?
Nothing to change the side of the DB.
To be safe, you just have to see how its stored your images.
;)