As I understand it for each version defined in an uploader CarrierWave will copy original file from cache to tmp path and hand over this tmp file to whatever processing is defined for this version. Then it can store all files. Sometimes (e.g. when generating thumbnail for videofile) this copying can be prohibitively expensive. Can I make CarrierWave to not copy and to let me generate versions from the original file while it's in cache?
Edit I have move_to_cache and move_to_store to return false true (oops I forget my own head soon). And I wrote a test processing module on the lines of CarrierWave::RMagick:
module CarrierWave
module Thumbnailer
def generate_thumbnails
debugger
x = 1
end
end
end
and I have the following lines inside the uploader
version :thumb do
process :generate_thumbnails
end
But when execution is stopped on the debugger (that's where I can start processing) CarrierWave has already copied and renamed the uploaded file. I can see both of them inside the cache directory.
Check out the readme under Large Files ... from the README:
class MyUploader < CarrierWave::Uploader::Base
def move_to_cache
true
end
def move_to_store
true
end
end
When the move_to_cache and/or move_to_store methods return true, files
will be moved (instead of copied) to the cache and store respectively.
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 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
in my RoR project I am using CarrierWave + MiniMagick and deployed to Heroku for production.
I have this problem in production that sometimes the tmp file is missing for image processing. I get this error:
Errno::ENOENT: No such file or directory - /tmp/mini_magick20130319-2-3wq6l6.jpg
I have other XUploader classes that works but this particular one have two image processes. Initially I had two separate process for this:
process :resizer
def resizer
resize_to_fit(model.jrac_image_width, model.jrac_image_height)
end
process :cropper
def cropper
manipulate! do |img|
img.crop("442x190+#{model.jrac_crop_x}+#{model.jrac_crop_y}")
img
end
end
but it said I was having error on :cropper saying the tmp file doesn't exist. I tried to change the code to this, hopefully it will only work on it once:
process :resize_and_crop
def resize_and_crop
manipulate! do |img|
img.resize("#{model.jrac_image_width}x#{model.jrac_image_height}") # resize_to_fit
img.crop("442x190+#{model.jrac_crop_x}+#{model.jrac_crop_y}") # cropper
img
end
end
but unfortunately, still experiencing the same errors.
Does anyone have any idea where the problem is? I don't know if it's with Heroku or CarrierWave or ImageMagick?
Edit
I also have this code on my Uploader class
def cache_dir
"#{Rails.root}/tmp/uploads"
end
as for this document.
heroku will empty the tmp from time to time, usually, we use s3 or other cloud storage to store the processed version. You may refer to https://github.com/jnicklas/carrierwave and
https://github.com/jnicklas/carrierwave/wiki/How-to%3A-Make-Carrierwave-work-on-Heroku
They have detailed walkthrough on using carrierwave in heroku
I have tried that before and it works
I'm using carrierwave in a rails 3 application to upload and store a file from a remote source in my server's file system. I've got a setup that's totally standard, with an uploader mounted on the model that the image is associated with.
It works perfectly 99.9% of the time, but every 600th graph or so I run into an issue where the app persistently fails to serve the stored image. If I check on the filesystem, the graph image has been uploaded and stored in the correct location, with the correct file permissions and everything, but rails is totally unaware of it and continues to serve the default graph image for that instance.
In other words a graph with id 123 has it's image stored at /uploads/graphs/123/graph.png ... the correct image is there but as far as rails is concerned it has no image stored. All the other graph images still work fine, but I can't get rails aware of the image stored for 123. Removing and re-storing the image doesn't work. Manually removing the image and re-uploading doesn't work. I'm totally lost. The graph instance is valid, no errors in the logs when I save.
for example, in the console:
g = Goal.find_by_id("123")
g.remote_graph_url = "http://image.source/url.png"
> "http://image.source/url.png"
g.save
> true
g.graph?
> false
g.graph_url >> /default/image.png
here's the relevant code:
class GraphUploader < CarrierWave::Uploader::Base
def store_dir
"../uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def default_url
"/images/" + [version_name, "default_large_graph.png"].compact.join('_')
end
def extension_white_list
%w(jpg jpeg gif png)
end
def filename
"graph.png" if original_filename
end
end
class Graph
mount_uploader :graph, GraphUploader
end
Wish I could just reply...
So you are saving uploads outside of the public directory? Try removing the "../" before images, if that wasn't your intention.
I'm using the carrierwave gem to upload files.
I have built a system for users to flag images as inappropriate and for admins to remove the images. From what I can tell, calling destroy on the image will only remove the path name from the table.
Is there a way to have carrierwave actually remove the file itself? Or should rails automatically remove the file when I destroy the image path?
Like #mu_is_too_short said, you can use File#delete.
Here's a code snippet you could use as a helper with a little tweaking in your rails app.
def remove_file(file)
File.delete(file)
end
or if you just have the filename stored in file
def remove_file(file)
File.delete("./path/to/#{file}")
end
Not sure what CarrierWave offers for this, but you could use FileUtils in the Ruby standard library with an ActiveRecord callback.
For instance,
require 'FileUtils'
before_destroy :remove_hard_image
def remove_hard_image
FileUtils.rm(path_to_image)
end
Sidenote: This code is from memory.
If one wants to delete a file but does not want to specify the full filename you can use the below.
Can also be used to delete many files or all files in a directory with a specific extension...
file = Rails.root.join("tmp", "foo*")
or
file = Rails.root.join("tmp", ".pdf")
files = Dir.glob(file) #will build an array of the full filepath & filename(s)
files.each do |f|
File.delete(f)
end