【ruby on rails】image_uploadプラグイン

画像をかっこよくアップロードするプラグインImageUploadを使ってみた。

ImageUpload:
http://imageupload.rubyforge.org/

参考:
http://cookpad.typepad.jp/lt/2007/12/image_upload621_947f.html
http://ko.meadowy.net/~nay/?image_upload%A5%D7%A5%E9%A5%B0%A5%A4%A5%F3

特徴:
画像の情報(パスやサイズ等)は独立したテーブルで管理し、他のモデルとポリモーフィック関連で関連づけられるので既存の親マスタに対して柔軟に設計可能。インタフェースは、ajaxを利用してファイル選択と同時にファイルをアップロードしプレビューでき、複数の画像を同時に操作できる(非ajaxインタフェースもあり)。画像アップロード字に画像の回転が行える。画像データは任意のディレクトリに保存し任意のフォルダから画像データを取得するヘルパーがある。

その他:
RMagickが必要。

いい感じで動きます。
アップロードした画像とそのサムネイルを出力する機能が欲しかったので弄ってみた。プラグインを触るのは大変そうなので画像データのActiveRecordモデルであるstored_fileモデルを弄って対応した。

# The base stored file class.
class UserImage < ActiveRecord::Base
  belongs_to :attachable, :polymorphic => true
  acts_as_list :scope => :attachable

  attr_accessor :buffer, :tmp_file, :thumbnail
  after_destroy :delete_file
  THUMBNAIL_WIDTH  = 40
  THUMBNAIL_HEIGHT = 40
  NORMAL_PATH  = '/normal/'
  THUMBNAIL_PATH  = '/thumbnail/'
  
  validates_presence_of :name, :unique_key
  
  def ext
    self.name =~ /.*\.(.*)$/
    $1
  end

  # Set the uploaded buffer.
  # This is useful for uploading without Ajax I/F.  
  def buffer=(buffer)
    @buffer = buffer
    return unless buffer?
    self.name = buffer.original_filename
    self.unique_key = genkey()
  end

  # Returns true if the buffer is valid.
  def buffer?
    @buffer && @buffer != "" && @buffer.original_filename.length > 0
  end
  
  def tmp_file=(tmp_file)
    @tmp_file = tmp_file
    return unless @tmp_file
    self.name = tmp_file[:name]
    self.unique_key = genkey()
  end

  # You can change this if you want to use another name pattern.
  def change_thumbnail
    self.thumbnail = true
    self.width  = THUMBNAIL_WIDTH
    self.height = THUMBNAIL_HEIGHT
  end
  
  def stored_file_path
#    File.join(path, stored_name)
    if self.thumbnail == true
      stored_file_path_thumbnail
    else
      stored_file_path_normal
    end
  end
  
  def image_path
    return $1 if stored_file_path.match(/public\/images\/(.*)$/)
    nil
  end

  def scale_down_size(max_width, max_height)
    return [nil, nil] unless self.width && self.height
    h = height.to_f / max_height.to_f
    w = width.to_f / max_width.to_f
    return [self.width, self.height] if (h <= 1 && w <= 1)
    
    m = h >= w ? h : w
    return [(self.width.to_f / m).to_i, (self.height.to_f / m).to_i]
  end
  
  protected

  def before_save
    self.stored_name = create_file_name
  end

  def before_create
    if @tmp_file
      File::open(@tmp_file[:path], 'rb') do |fh|
        update_file_attributes(i = ImageSize.new(fh))
        i.close # for windows
      end
    else
      update_file_attributes(ImageSize.new(@buffer))
    end
  end
  
  def after_create
#    FileUtils.makedirs(path) unless File.exist?(path)
    FileUtils.makedirs(path + NORMAL_PATH) unless File.exist?(path + NORMAL_PATH)
    FileUtils.makedirs(path + THUMBNAIL_PATH) unless File.exist?(path + THUMBNAIL_PATH)
    if @tmp_file
      FileUtils.mv(@tmp_file[:path], stored_file_path_normal)
      raise "Couldn't move #{@tmp_file[:path]}" unless File.exist?(stored_file_path_normal)
      resize_and_write_image(stored_file_path_normal, stored_file_path_thumbnail)
      
      # delete original tmp file (if exists)
      File.delete(@tmp_file[:original_tmp_path]) if @tmp_file[:original_tmp_path] && File.exist?(@tmp_file[:original_tmp_path])
      @tmp_file = nil
    else
      store_buffer
      @buffer.close # Avoid TempFile's garbage collection problem (exception arount closed stream).
      @buffer = nil
    end
  end

  private
  # Create file_name from unique_key. 
  # Some file name (ex. using Japanese) can't be displayed properly by some OS.
  # And, this can disturb users to image and try other file names.
  def create_file_name(ext = nil)
    ext ||= self.ext
    return name unless ext
    "#{unique_key}.#{ext}"
  end
  
  # Store the file in the file system.
  def store_buffer
    return unless buffer?
    @buffer.binmode
    File.open(stored_file_path_normal, "w") do |f|
      f.binmode
      f.write(@buffer.read)
      resize_and_write_image(stored_file_path_normal, stored_file_path_thumbnail)
    end
  end
  
  # Delete the file from the file system.
  def delete_file
#   File.delete(stored_file_path)
    File.delete(stored_file_path_normal) if FileTest.exist?(stored_file_path_normal)
    File.delete(stored_file_path_thumbnail) if FileTest.exist?(stored_file_path_thumbnail)
  end

  # You can overwrite this.    
  def path
    "#{RAILS_ROOT}/attached_files/user_images"
  end

  GENKEY_MAX_TRY = 10

  def exsit_key?(key)
    UserImage.find(:first, :conditions => ['unique_key = ?', key]) ? true : false
  end


  def genkey
    salt = [rand(64),rand(64)].pack("C*").tr("\x00-\x3f","A-Za-z0-9./")
    n = 0
    key = nil
    begin
      raise "genkey_max_try over." if n == GENKEY_MAX_TRY
      key = "#{Time.now.strftime('%Y%m%d%H%M%S').crypt(salt).tr(' /.', '___')}-#{n}"
      n += 1
    end while exsit_key?(key)
    key
  end

  def update_file_attributes(image_size)
    raise "no ImageSize" unless image_size
    self.mime_type = image_size.mime_type
    if size = image_size.get_size
      self.width = size[:width]
      self.height = size[:height]
    else
      self.width = nil
      self.height = nil
    end
    self.thumbnail = false
  end  

  def stored_file_path_thumbnail
    File.join(path, THUMBNAIL_PATH + stored_name)
  end

  def stored_file_path_normal
    File.join(path, NORMAL_PATH + stored_name)
  end

  def resize_image(img, width, height)
    return true if img.columns <= width && img.rows <= height
    begin
      require 'RMagick'
      img.resize_to_fit!(width, height)
      return true
    rescue Exception => e
      return false
    end
  end

  def resize_and_write_image(source, dest)
    begin
      result = true
      require 'RMagick'
      img = ::Magick::Image::read(source).first
      if resize_image(img, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT)
        img.write(dest)
      else
        result = false
      end         
      img = nil
      enabled_gc = GC.enable
      GC.start
      GC.disable if enabled_gc        
      return result
    rescue Exception => e
      return false
    end
  end
end


かなりいけてない修正になった感があるが、まあいいとしよう。

タグ:[ruby on rails

このログへのコメント(0件)

コメント投稿フォーム

コメント

※半角英数字だけのコメントは投稿できません。

トラックバック

関連ログ