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.
Related
I use Carrierwave to do file uploads in my Rails app.
When replacing an already uploaded image, the browser still renders the cached original image to the user. Only after hard-refreshing (Cmd-Shift-R on Mac) it renders the new one.
I read here (Stop images from caching in Rails and browser?) that Rails would attach the image's "last updated" timestamp to the URL, but it seems it doesn't in my app:
Maybe this changed meanwhile (the mentioned post is from 2009)? And how would that be fixed today? Thank you.
image_tag uses asset_path to calculate final image path (in case it is from asset pipeline), but neither of those add cache buster timestamp.
When image is in asset pipeline and asset digests are enabled - a checksum will be added to file name by asset compilation. For uploaded images it's a task of your image uploading library or yourself.
For example (popular in the past) library paperclip used to save image timestamp in <attachment>_updated_at attribute and add it to generated links.
For carrierwave it's possible to include timestamp in file name at upload like:
class PhotoUploader < CarrierWave::Uploader::Base
def filename
#name ||= "#{timestamp}-#{super}" if original_filename.present? and super.present?
end
def timestamp
var = :"##{mounted_as}_timestamp"
model.instance_variable_get(var) or model.instance_variable_set(var, Time.now.to_i)
end
end
Or make it generate unique filenames by some other algorithm.
Anyway using original user-provided filename in your storage is usually not a good idea from security point of view (for example someone can upload nasty_xss_hack.html).
Other option - is to add timestamp yourself, model's updated_at.to_i is not ideal (usually model is updated more othed than the image), but will have the job done.
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 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?
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
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.