Elsewhere > Large Images in Rails
Posted 2012-09-18 on viget.com
The most visually striking feature on the new WWF site, as well as the source of the largest technical challenges, is the photography. The client team is working with gorgeous, high-fidelity photographs loaded with metadata, and it was up to us to make them work in a web context. Here are a few things we did to make the site look and perform like a veritable snow leopard.
Optimize Images
The average uploaded photo into this system is around five megabytes, so
the first order of business was to find ways to get filesize down. Two
techniques turned out to be very effective:
jpegtran and
ImageMagick’s quality
option. We run all photos through a custom
Paperclip processor that
calls out to jpegtran to losslessly optimize image compression and strip
out metadata. In some cases, we were seeing thumbnailed images go from
60k to 15k by removing unused color profile data. We save the resulting
images out at 75% quality with the following Paperclip directive:
has_attached_file :image,
:convert_options => { :all => "-quality 75" },
:styles => { # ...
Enabling this option has a huge impact on filesize (about a 90% reduction) with no visible loss of quality. Be aware that we’re working with giant, unoptimized images; if you’re going to be uploading images that have already been saved out for the web, this level of compression is probably too aggressive.
Process in Background
Basic maths: large images × lots of crop styles = long processing time.
As the site grew, the delay after uploading a new photo increased until
it became unacceptable. It was time to implement background processing.
Resque and
delayed_paperclip to
the … rescue (derp). These two gems make it super simple to process
images outside of the request/response flow with a simple
process_in_background :image
in your model.
A few notes: as of this writing, delayed_paperclip hasn’t been updated recently. Here’s a fork that works from tommeier. I recommend using the rescue-ensure-connected gem if you’re going to run Resque in production to keep your long-running processes from losing their DB connections.
Server Configuration
You’ll want to put far-future expires headers on these photos so that browsers know not to redownload them. If you control the servers from which they’ll be served, you can configure Apache to send these headers with the following bit of configuration:
ExpiresActive On
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
(Similarly, for nginx.) When working with a bunch of large files, though, you’re probably better served by uploading them to S3 or RackSpace Cloud Files and serving them from there.
Another option to look at might be Dragonfly, which takes a different approach to photo processing than does Paperclip, resizing on the fly rather than on upload. This might obviate the need for Resque but at unknown (by me) cost. We hope that some of this will be helpful in your next photo-intensive project.