This is my first code dealing with uploads and rmagick, so please comment if you have suggestions.
class DbFile < ActiveRecord::Base IMAGE_TYPES = ['image/jpeg', 'image/pjpeg', 'image/gif', 'image/png', 'image/x-png'] before_validation :sanitize_filename validates_presence_of :size, :filename, :content_type class << self def new_file(file_data) content_type = file_data.content_type.strip (IMAGE_TYPES.include?(content_type) ? DbImage : DbFile).new \ :data => file_data.read, :filename => file_data.original_filename, :size => file_data.size, :content_type => content_type end end protected def sanitize_filename # NOTE: File.basename doesn't work right with Windows paths on Unix # get only the filename, not the whole path filename.gsub! /^.*(\\|\/)/, '' # Finally, replace all non alphanumeric, underscore or periods with underscore filename.gsub! /[^\w\.\-]/, '_' end end require 'rmagick' require 'base64' class DbImage < DbFile def data=(file_data) with_image(file_data, true) do |img| self.width = img.columns self.height = img.rows end end def with_image(file_data = nil, save_image = false, &block) img = Magick::Image::read_inline(Base64.b64encode(file_data || self.data)).first block.call(img) write_attribute('data', img.to_blob) if save_image img = nil GC.start end end
Controller Usage:
# returns DbImage if content_type matches db_file = DbFile.new_file(params[:file][:data]) db_file.save
Model Usage:
# raw binary image data File.open('my_file', 'w') { |f| f.write(db_file.data) } # Image resizing with rmagick # automatically creates RMagick::Image and # invokes GC.start db_file.with_image do |img| img.scale(.25) img.write('thumb.jpg') end