Back in the day, I used CarrierWave and MiniMagick to handle my image upload and resizing needs, and for the most part it did a swell job. I even had a pretty decent setup where the users would upload their images directly to S3, then a Heroku Worker Dyno would do the handle the actual image manipulation, all cached by CloudFront.
However, every once in a while I’d get a weird memory usage spike. Normally caused by a user uploading a remarkably large image and my worker trying to resize the various versions (Some of which, probably wouldn’t even be seen) as quickly as possible.
The memory spikes were easily fixed by better sidekiq configuration, but it really ate away at me that I was creating images that were seldom used.
Resizing on the fly in the cloud
There are a few SaaS providers who handle just this use case, the two main ones I liked were Cloudinary and Fastly. I’m a big fan of Cloudinary as it has a really nice Ruby Gem with a CarrierWave integration, but I’d encourage you to explore both options.
It’s pretty easy to setup to! Just mount your uploader as normal:
# app/models/user.rb class User < ApplicationRecord # Mount your uploads as usual. mount_uploader :avatar, User::AvatarUploader end
Then setup your uploader with your versions:
# app/uploaders/user/avatar_uploader.rb class User::AvatarUploader < ApplicationUploader # http://res.cloudinary.com/demo/image/upload/c_fill,f_auto,h_180,q_auto,w_180/sample.png version :default do eager #process resize_to_limit: [150, 150] process resize_to_fill: [180, 180] cloudinary_transformation quality: 'auto', fetch_format: 'auto' end # http://res.cloudinary.com/demo/image/upload/c_fill,h_48,r_max,w_48/sample.ico version :favicon do process convert: 'ico' process resize_to_fill: [48, 48] cloudinary_transformation radius: 'max' end # http://res.cloudinary.com/demo/image/upload/c_fill,h_114,w_114/sample.png version :apple_touch do process convert: 'png' process resize_to_fill: [114, 114] end end
You can link to the image version by passing the version type in the
url method. You can also use the
cl_image_upload method to upload directly to Cloudinary servers, skipping having the data stop at your server on the way to Cloudinary.
<!-- app/views/users/_form.html.erb --> <%= image_tag form.object.avatar.url(:default) if form.object.avatar.present? %> <%= form.cl_image_upload(:avatar, allowed_formats: %w(jpg jpeg gif png)) %>
Easy as pie right?! I’ve setup a fresh Rails 5.1 project, which shows an example integration which can be deployed to Heroku.
Cloudinary, like most SaaS can begin to feel a little bit expensive if you’re not utilizing a large percentage of your plan. However as mentioned above Fastly is a reasonable alternative. If you’re really scaling up, it’s also possible to resize images via AWS Lambda.
As pointed out by a reader MiniMagick is just a Ruby wrapper for C functionality. So more accurately, this could have been more accurately titled “Stop resizing images with C” or “Stop resizing images yourself”.