Have now needed to do this twice, and both times have required about an hour of sifting through Google morass to figure out how to pull this off. The situation we’re assuming here is that you have some CarrierWave file that you’ve stored to a remote location, and you want to get a copy of that file locally to manipulate it. With promising method names like “retrieve_from_cache!” and “cache!” and “move_to_cache” you too may become entangled in the maze of what the hell you’re supposed to be calling to accomplish this. Here’s what.
Step 1: Retrieve from your store (the external place your file exists) to cache (your local machine).
my_cw_uploader.cache_stored_file!
After running that, if you call
my_cw_uploader.cached?
You should get a long string that is the filename for the file that got cached. Note that the cache status of a file is only saved on a per-object basis, so if you try something like
Image.find(1).my_cw_uploader.cache_stored_file!
… you will never again see the object that did the caching, and you will never be able to use that cache. Only call #cache_stored_file! on an object you are keeping around.
Step 2: Go access the file that you cached. Not quite as easy as it sounds. For this, I mixed in the following into my uploader class
module CarrierWave::Uploader::Cache
def full_cache_path
"#{::Rails.root}/public/#{cache_dir}/#{cache_name}"
end
end
So now when you call
my_cw_uploader.full_cache_path
You’ll get the full pathname to the file you downloaded.
Hope this helps save someone else from an hour of Google hell.
Thanks for the great blog post Bill! 🙂
I’m trying to use CarrierWave cache with Heroku. The problem is when I have more than 1 dyno, they don’t know about each other public/cache folders and could not find the file. How do you deploy your app? Did you see the same problem?
Thank You. I probably spent over two hours searching the Internets and other crap, and then in frustration finally looking at the code, of which I spent too much time. Then I stumbled upon this post. A sincere thank you.
Just for anybody who may be wondering:
seems to be nil until after the call of #cache_stored_file! I imagine there has got to be some reason why, but I don’t know. Apparently, from looking at the code, #filename is meant to be overridden to something more reasonable than the actual name of the file the user may have selected for upload. That was sarcasm.
Thank you so much for this, I was really scratching my head this morning trying to get this to work.
My use case was I was using images pulled in from S3 (uploaded by CarrierWave) and using them in a canvas element, which even with CORS settings enabled on S3 was causing security exceptions, caching the file locally and accessing it that way bypasses all these issues.
Incidentally, once you’ve called cache_stored_file! you can actually just call uploader.file to get a File like object, and uploader.path to get the path to it. No need to resort to a mixin to get the path :). But this part of the API is indeed fairly confusing. Doubly confusing for me because I mounted all my uploaders as file so I have to model.file.file to get at the File.
Actually, when you call
cache_stored_file!
you can then retrieve the path by callingobject.url
.For example, assuming you have a
User
class with an avatarmount_uploader :avatar, AvatarUploader
you can do this:user = User.find(1)
file = user.avatar
cached_file = file.cache_stored_file!
path = cached_file.url # => /uploads/tmp/1382623110-73980-2669/filename.jpg
I found an alternative way to save a local copy using callbacks. I just put this in my uploader class:
before :store, :save_test_image
def save_test_image(file)
if Rails.env == 'test'
src = model.image.path
dest = File.join(Rails.root, 'test', 'images', 'test_cached_image.png')
FileUtils.cp(src, dest) unless File.exists?(dest)
end
end
It just copies the cached version of the file from the local hard drive into a test folder before carrierwave deletes everything in the cache.