Forum: Ruby on Rails Picture uploading / validating / resizing

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Martin S. (Guest)
on 2005-12-01 17:05
Hello,

I'm researching various options to handle picture
uploading/validating/resizing in  Rails.

I plan to have one table "pictures" (model Picture) which will store all
pictures. These pictures can then be assigned to other models through
associations.

Here is the process:

1. User browses the picture and uploads it.
2. Picture's content-type and size are validated (e.g. max 1MB size)
3. Picture is autmatically resized to a given size (e.g. 700x500) and
converted to JPG. This resulting JPG is stored in the file system as
"original". The path pointing to the picture should be
"/public/pictures/<picture.id>/original.jpg". Temp folder should be used
for form redisplays in some situations.
4. Views can request original jpg or some modified version (using
rmagick modifications) - e.g. view can request 200x100 version of the
original. These modified versions are cached in the file system.

I've started to play with file_column and ImageMagick for Rails.

Can this scenario be accomplished using these 2 extensions? Or do you
know about some other helpful resources?

Thanks for any help ;)
smedberg (Guest)
on 2005-12-01 19:15
(Received via mailing list)
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.
# Visit http://www.pragmaticprogrammer.com for more book information.
#---
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 "<img src='/images/noimage.jpg'/>"
    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
This topic is locked and can not be replied to.