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