I’m doing something very similar (I do thumbnailing for images that
represent users), but I’m not using file_column, I’m just using a BLOB
column. I haven’t done any scale testing yet, so I might have to switch
to
some file caching scheme in the future.
I made some small additions to the Picture class from the Agile book.
Here’s the code:
#—
Excerpted from “Agile Web D. with Rails”
We make no guarantees that this code is fit for any purpose.
#—
class Picture < ActiveRecord::Base
validates_format_of :content_type, :with => /^image/,
:message => “— you can only upload pictures”
#MES- TODO: We may want to delay loading the data column- loading the
data
just to get info like the extension might get expensive.
DEFAULT_THUMBNAIL_HEIGHT = 50.0
MAX_IMAGE_SIZE = 1 * 1024 * 1024
def picture=(picture_field)
self.name = base_part_of(picture_field.original_filename)
self.extension = File.extname(self.name)
self.content_type = picture_field.content_type.chomp
self.data = picture_field.read
end
def check_valid
#MES- Check that the data isn’t too big
if MAX_IMAGE_SIZE < data.length
raise “Image too large”
end
#MES- Check that the blob we contain is an image
return false if !load_image_lib
begin
img = Magick::Image.from_blob(data)
return true
rescue
#MES- If we get an exception converting it to an image, then it's
not
good
raise “Image format not recognized”
end
end
def base_part_of(file_name)
name = File.basename(file_name)
name.gsub(/[^\w._-]/, ‘’)
end
def create_thumbnail(height = DEFAULT_THUMBNAIL_HEIGHT)
#MES- Only do thumbnailing if the Image Magick library can be
loaded.
# This is to make setup easier for other developers- they are not
# required to have Image Magick.
# More information on Image Magick is available at
# http://studio.imagemagick.org/RMagick/doc/usage.html
if load_image_lib
#MES- Turn the blob into an ImageMagick object
img = Magick::Image.from_blob(data).first
if img.nil?
logger.info “Failed to make thumbnail for image #{self.name}-
unable
to create RMagick wrapper for image”
return nil
end
#MES- Shrink the image
thumbnail = img.thumbnail(height/img.rows.to_f)
#MES- Store it into a new Picture object and return it
return Picture.create(:name => self.name, :extension =>
self.extension,
:content_type => self.content_type, :data => thumbnail.to_blob)
else
return nil
end
end
def load_image_lib
begin
#MES- Note that “require” caches values, so calling it multiple
times
should NOT cause the
# library to be loaded multiple times.
require ‘RMagick’
return true
rescue MissingSourceFile
logger.info ‘In Picture::load_image_lib. The RMagick library
could
not be loaded, so image manipulations will not be performed.’
return false
end
end
end
and here’s the schema I’m using:
CREATE TABLE pictures
(
id
<%= @pk %>,
name
varchar(255) default NULL,
extension
varchar(255) default NULL,
content_type
varchar(255) default NULL,
data
blob,
created_at
<%= @datetime %>,
updated_at
<%= @datetime %>
) <%= @options %>;
To display the image, I put methods into my controller, like this:
def image
@user = User.find(params[:id])
send_image(@user.image)
end
def thumbnail
@user = User.find(params[:id])
send_image(@user.thumbnail)
end
def send_image(img)
send_data(img.data, :filename => img.name, :type =>
img.content_type,
:disposition => ‘inline’)
end
and I generate URLs like this:
def build_user_thumbnail_url(user)
if !user.thumbnail.nil?
return image_tag(url_for(:controller => ‘user’, :action =>
‘thumbnail’, :id => “#{user.id}#{user.thumbnail.extension}”))
else
return “”
end
end
Finally, I add to my routes.rb to handle various image extensions:
#MES- Map any file extension for user ‘image’ or ‘thumbnail’ to the
action
map.connect ‘user/image/:id’, :controller => ‘user’, :action =>
‘image’,
:id => /^...$/
map.connect ‘user/thumbnail/:id’, :controller => ‘user’, :action =>
‘thumbnail’, :id => /^...$/
I hope that helps