How to upload while resizing the original image itself in Shrine - ruby-on-rails

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.

Related

Carrier Fog Aws - Files are automatically disappearing from S3

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

Dynamic store dirs for same models with carrierwave

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

Saving image created with RMagick using Rails and CarrierWave

In my Rails app I have a module that takes several images, and using RMagick "stitches" them together into a new single image. I'm able to successfully create the final image, but I'm having trouble saving this new image as an attachment to a model (using CarrierWave). The method that does the stitching looks like this:
def generate_collage(params)
final_image = ImageList.new
# ... code that puts together the composite image ...
return final_image.append(true).to_blob { |attrs| attrs.format = 'JPEG' }
end
I've got my User model with an uploader mounted:
class User < ActiveRecord::Base
mount_uploader :image, UserImageUploader
end
In the CarrierWave documentation under the ActiveRecord section, they show how to assign a new image, but they assume the file already exists somewhere. In my case it doesn't yet exist on the filesystem, and I'm outputting a blob... is there any way to go from that blob to generating an image upload for CarrierWave?
I suppose I'm trying to avoid saving this image temporarily into "#{Rails.root}/tmp/" and then reading it from there... it seems like I could cut out this step and send directly to CarrierWave somehow, but I don't know how! Is it possible?
I'm working on something similar right now. This should be possible, but an easy workaround is to save it to a temp file:
temp_file = Tempfile.new([ 'temp', '.png' ])
image.write(temp_file.path)
user = User.new
user.avatar = temp_file
user.save
temp_file.close
temp_file.unlink
I'm hoping to try to improve it to remove the file system dependency completely, by following the advice in one of these answers: How to handle a file_as_string (generated by Prawn) so that it is accepted by Carrierwave?

Changing location of files stored using carrierwave

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.
;)

Paperclip Processor Operate on S3

I am trying to create a custom Paperclip::Processor that integrates with an external web service (the processor will call the web service whenever a new file is uploaded). The external service needs the file to be present in S3 and will handle uploading the processed versions to S3 automatically.
Can this be done using a custom Paperclip::Processor or should it be done with an ActiveRecord callback? If a Paperclip::Processor will work, what is the best way to trigger the upload? Ideally I'd like to do a processor, but the requirement is that the original file MUST be uploaded to S3 first. I have taken a look at using after_create calls, but it sometimes seems to conflict with the after_create used in paperclip. Thanks.
You can do this to create a local copy of the file. If it's on S3 it will be downloaded.
tmp_file = #model.attached_file.to_file => TempFile<...>
You can then do your operations on this TempFile. When you're don:
#model.attached_file = tmp_file
#model.save
Edit: misread your question. You can use the before_post_process and after_post_process hooks to perform tasks before or after the file was processed.
class Model < AR::Base
has_attached_file :avatar
after_post_process :ping_webservice
private
def ping_webservice
# Do your magic here.
end
end
I dealt with a similar issue recently, and it was with the after_save callback. I managed to fix my problem by defining paperclip (has_attached_file ...) after I defined my after_save. This way, paperclip's callback will fire after mine.

Resources