A couple of days ago I wrote about uploading attachments and in this tutorial I'm going to look at doing some processing on those attachments once they have been uploaded. Specifically, resizing any images to a suitable size to be rendered in a web browser.

For this tutorial, we're going to be using the Attach and Lizard gems. The attach gem has already been covered in the other tutorial and we'll be using it's post-processing feature to handle resizing.

I'll assume that you've read the tutorial about attaching files. If not, got an take a quick look.

Dependencies

Lizard works with ImageMagick. You'll need to make sure you have ImageMagick available on your computer and servers.

  • If you're a macOS user, you can install ImageMagick using Homebrew.
  • If you're a Linux user, ImageMagick exists in both apt and yum software repositories.

The naming for the Lizard gem goes something along these lines: ImageMagick -> Magic -> Wizards -> Lizards -> Lizard. Somewhat of a tenuous link, at best!

Validating we have an image

The first step will be validate that the image a user uploads to us is actually an image and that the size isn't too large. T

class User < ActiveRecord::Base
  attachment :profile_picture do
    validator do |attachment, errors|
      if attachment.data.bytesize > 5.megabytes
        errors << "image is too large (maximum size is 5MB)"
      elsif !Lizard::Image.is_image?(attachment.binary)
        errors << "must be an image"
      end
    end
  end
end

The lizard gem provides us with a handy is_image? method which accepts some binary data and tells if it represents an image. In this case, we're going to add an error if it says it isn't an image.

Creating thumbnails

The next step will be to create a thumbnail. We'll be creating a thumbnail for 1400x1400 which is suitable for our use case. In your case, you may wish to choose a different size or create multiple sizes.

processor do |attachment|
  # Create an image instance for the image that was uploaded
  image = Lizard::Image.new(attachment.binary)
  # Resize and return a new image instance for the resized version of the image
  thumbnail = image.resize(1400, 1400)
  # Create a child attachment containing the new file
  attachment.add_child(:thumbnail) do |child|
    child.binary = thumbnail.data
    child.filename = "thumbnail1400.jpg"
  end
end

That's all there is to it. Whenever you upload an image, a resized version will also be stored alongside your original version.

Accessing the thumbnail

To access the thumbnail once uploaded, you can use the child method on your attachment. The example below shows inserting an image into a document while using the thumbnail's image. It will only be rendered if a profile picture has been uploaded and a thumbnail child has been added.

<% if current_user.profile_picture && current_user.profile_picture.child(:thumbnail) %>
  <%= image_tag current_user.profile_picture.child(:thumbnail).url %>
<% end %>

Storing dimensions

Another useful concept is storing the dimensions of an uploaded image in your database. I have found this useful when working with Facebook's Open Graph which requires the width & height of an image to be provided when giving them an image URL. To start storing these, we can extend our processor like so.

processor do |attachment|
  image = Lizard::Image.new(attachment.binary)
  attachment.custom['width'] = image.width
  attachment.custom['height'] = image.height
end

These can then easily be inserted when needed:

<% if image = current_user.profile_picture %>
  <meta name="og:image" content="<%= image.url %>" />
  <meta name="og:image:width" content="<%= image.custom['width'] %>" />
  <meta name="og:image:height" content="<%= image.custom['height'] %>" />
<% end %>

Do your processing in the background

Image resizing can take some time depending on your computer and the size of the original image. It is recommended to move all image processing into the background. Attach supports this and allows you to easily pass off the processing of your attachments to a background worker of choice. Check out the README for information about how to do this.

Next steps

Both Attach gem and the Lizard support numerous other functions which are out of the scope of this tutorial. It's worth checking out the README files for both repositories to understand everything you could be doing.

Tell us how you feel about this post?