Thumbnails with RMagick

Posted by Curtis Miller Curtis Miller

I continue to prototype my site in order to have something to show potential investors. Recently, I tackled uploading images into a user's gallery. The ability to upload images was pretty straightforward. You create a form that has a file_field element. For example, you may have something like this:

<%= form_tag({:action => 'gallery'}, :multipart => true) %>
  <label for="picture_comment">Comment</label><br />
  <%= text_field('picture', 'comment') %>
  <%= file_field('picture', 'picture') %><br />
  <%= submit_tag("Upload") %>
</form>

Notice that we set multipart to true which results in enctype="multipart/form-data". Without this, I don't think you'll get anything on submit.

The controller code for the gallery action is pretty straightforward too:

def gallery
  # Create a new Picture object from the parameters
  @picture = Picture.new(params[:picture])

  # Try to save the picture.
  begin
    @picture.save!
  rescue
    flash.now[:notice] = "Unable to upload picture"
  end
end

There is some more information on this, and a few other related things, in the Ruby on Rails manual, so I won't go into more detail.

What I noticed with my simplistic approach is that I needed to scale images for display, but instead of doing something like setting the width and height of the image tag, I looked at RMagick. RMagick is a Ruby interface to the ImageMagick or GraphicsMagick library.

The first thing I have to say about the ImageMagick and GraphicsMagick libraries is they have a lot of dependencies. This is good if you need to support a million different file formats, most of which you've never heard of. Not so good if you are just prototyping an application. I chose to try ImageMagick first.

Unless you want to download, build (possibly), and install all of the dependencies then you need to get the source tarball and build ImageMagick yourself. Part of the configure script detects the libraries you have installed and sets up the makefile accordingly. Now I have a scaled down version of ImageMagick to use for prototyping.

The Rails Recipes book has a recipe (#57) that describes a way to create separate thumbnails for each image that is uploaded. This looks great, but I found that particular recipe was only good for images greater than the “maximum” size. If an image was smaller, then it stretched it out of shape. Not good.

Building on the RoR recipe above, I modified it slightly to do a better job for images of all sizes. The approach is very simple and may not be suitable for all needs, but it suits me for now. I modified the create_thumbnail method to the following:

def create_thumbnail
  img = Magick::Image.read(path).first

  scaling_factor = MAX_HEIGHT / img.rows
  size = [img.columns * scaling_factor, img.rows * scaling_factor]

  thumbnail = img.thumbnail(*size)
  thumbnail.write thumbnail_path
end

This will force the created thumbnails to all have the same height, MAX_HEIGHT. To get there, we must apply the scaling factor to the image height (rows). To keep the image proportional, I also applied the scaling factor to the image width (columns). This allows me to maintain nice rows of images since all of their heights are the same. They may have differing widths, but that's okay if it keeps the image proportional.

The imageMagick library looks very robust and powerful and RMagick looks like a great interface to it. I look forward to working with it more. Let me know what you think about this approach.



Velocity Labs

Need web application development, maintenance for your existing app, or a third party code review?

Velocity Labs can help.

Hire us!