Custom thumbnails for file types with Paperclip

Chelsea picture Chelsea · Jul 30, 2009 · Viewed 7.4k times · Source

I'm using Paperclip with a Ruby on Rails to attach assets to a model, these assets can be any file type and currently thumbnails are only being generated if the asset is an image. I'd like to be able to display a different default image for other files, either by generating a thumbnail of the files on upload, or setting something up with the default_url but so far I can't find any resources to help with this and am getting no where on my own.

My model is as follows:

  class Asset < ActiveRecord::Base  
    has_attached_file :media,  
    :storage => :s3,  
    :s3_credentials => "#{RAILS_ROOT}/config/s3.yml",  
    :path => ":attachment/:id/:style.:extension",  
    :bucket => S3_BUCKET,  
    :styles => {:thumb => "75x75>", :large => "600x800>",  
    :whiny => false,  
    :default_url => "/images/:attachment/missing.jpg"  

Does anyone have any resources for generating custom thumbnails if generation fails, or fall back on something like :content_type in the default url? I've looked through the source and haven't been able to get anywhere.

Thanks!

Answer

Olly picture Olly · Jul 31, 2009

I've actually implemented this very same feature. Paperclip generates thumbnails for all my images and PDFs, and I have added custom thumbnail icons for MS Word, Excel, HTML, TXT files etc.

My solution is fairly straightforward. In my model Attachment (in your case Asset) I have defined the following method:

def thumbnail_uri(style = :original)
  if style == :original || has_thumbnail?
    attachment.s3.interface.get_link(attachment.s3_bucket.to_s, attachment.path(style), EXPIRES_AFTER)
  else
    generic_icon_path style
  end
end

This returns either a URL to a thumbnail stored on S3, or a local path to a generic PNG icon based on the assets content type (discussed below). The has_thumbnail? method determines whether or not this asset has had a thumbnail generated for it. This is something I added in my own fork of Paperclip, but you can substitute in your own logic (I'm not sure of the 'standard' way to determine this, maybe comparing the path with your defined 'missing' path, or even just comparing the content type to a default list ["image/jpeg", "image/png"] etc).

Anyway, here's the method which passes back a path to a generic icon based on both the thumbnail style (in your case :thumb and :large) and the content type:

# Generates a path to the thumbnail image for the given content type 
# and image size.
#
# e.g. a :small thumbnail with a content type of text/html, the file name 
#      would have the filename icon.small.text.html.png
#
# If no such thumbnail can be found a generic one is returned
def generic_icon_path(style = image.default_style)
  url = "/images/attachments/icon.#{style.to_s}.#{attachment_content_type.sub('/', '.')}.png"
  if File.exists? "#{RAILS_ROOT}/public/#{url}"
    url
  else
    "/images/attachments/icon.#{style.to_s}.default.png"
  end
end

Then, to add a new thumbnail I just add PNG files into /images/attachments/ with the correct file name convention. My thumbail style is called :small and I have defined styles for Word, Excel and plain text so at the present time I have:

icon.small.application.msword.png
icon.small.text.plain.png
icon.small.application.vnd.ms-excel.png
icon.small.application.vnd.openxmlformats-officedocument.spreadsheetml.sheet.png
icon.small.application.vnd.openxmlformats-officedocument.wordprocessingml.document.png

If the content type isn't supported, there's a generic 'catch all' icon which is displayed:

icon.small.default.png