Howto: Store a CarrierWave File Locally After Uploading to S3

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.

7 Replies to “Howto: Store a CarrierWave File Locally After Uploading to S3”

  1. 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?

  2. 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.

  3. Just for anybody who may be wondering:

    my_cw_uploader.filename 
    

    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.

  4. 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.

  5. 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.

  6. Actually, when you call cache_stored_file! you can then retrieve the path by calling object.url.

    For example, assuming you have a User class with an avatar mount_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

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

Leave a Reply to Zi Cancel reply

Your email address will not be published. Required fields are marked *