酒と泪とRubyとRailsと

Ruby on Rails と Objective-C は酒の肴です!

Carrierwave + Rails 4.2.5 画像アップローダー

今回は、carrierwave + rails 4.2.5.1で作る簡単画像アップローダーの作成ネタです。目次はこんなかんじです!

(1) carrierwave + rails 4.2.5.1で作る簡単画像アップローダーを作る
(2) simple_formと組み合わせる
(3) アップロード画像のサイズのvalidationを行う

前置き

(A) Model product.imageカラムをcarrierwave対応
(B) carrierwaveのアップローダはImageUploader

(1) carrierwave + rails 4.1で作る簡単画像アップローダーを作る

ImageMagickのインストール

Macの場合は以下のコマンドをコマンドラインから実行。

1
brew install imagemagick

Gemfileの登録

Gemfileに以下を追加して、bundle installを実行。

1
2
3
# Image Uploader
gem 'carrierwave'
gem 'rmagick', require: 'RMagick'

carrierwaveのアップローダーを作成

次のコマンドを実行すると、app/uploaders/image_uploader.rbが作成されます。

1
2
3
4
5
# scaffold で Productに関する一式を作成
rails g scaffold product name:string image:text

# アップローダーを作成
rails g uploader image

アップローダーの記述を修正

先ほど生成されたimage_uploader.rbを以下のように書き換えます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
  include CarrierWave::RMagick

  storage :file

  process convert: 'jpg'

  # 保存するディレクトリ名
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  # thumb バージョン(width 400px x height 200px)
  version :thumb do
    process :resize_to_fit => [400, 200]
  end

  # 許可する画像の拡張子
  def extension_white_list
    %w(jpg jpeg gif png)
  end

  # 変換したファイルのファイル名の規則
  def filename
    "#{Time.now.strftime('%Y%m%d%H%M%S')}.jpg" if original_filename.present?
  end
end

Modelのカラムにアップローダーを紐付け

Model Product.image と、アップローダー ImageUploader とをひも付け。

1
2
3
4
5
# app/models/product.rb
model Product
  # ↓ 以下を追加
  mount_uploader :image, ImageUploader
end

view/formへの追加

viewのformに以下を追加。

1
2
3
4
5
# app/views/products/_form.html.haml
form_for(@product) do |f|
  # ↓ 以下を追加
  = f.file_field :image
end

view 表示側への追記

アップロードした画像をviewで表示させる場合は以下を追記してください。

1
2
# app/views/products/show.html.haml
= image_tag @@product.image_url(:thumb)

以下を追加して、テーブルを作成。

1
bundle exce rake db:migrate

(2) simple_formでプレビュー表示させる場合

simple_form』はformの記述がスッキリするgemです。 このsimple_formcarrierwaveを連携させる手順です。

Gemfileの登録

Gemfileに以下を追加して、bundle installを実行。

1
2
# Image Uploader
gem 'simple_form'

以下のコードを書きます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# app/inputs/image_preview_input.rb
class ImagePreviewInput < SimpleForm::Inputs::FileInput
  def input
    # :preview_version is a custom attribute from :input_html hash, so you can pick custom sizes
    version = input_html_options.delete(:preview_version)
    out = '' # the output string we're going to build
    # check if there's an uploaded file (eg: edit mode or form not saved)
    if object.send("#{attribute_name}?")
      # append preview image to output
      out << template.image_tag(object.send(attribute_name).tap {|o| break o.send(version) if version}.send('url'))
    end
    # allow multiple submissions without losing the tmp version
    out << @builder.hidden_field("#{attribute_name}_cache").html_safe
    # append file input. it will work accordingly with your simple_form wrappers
    (out << @builder.file_field(attribute_name, input_html_options)).html_safe
  end
end

でもって form 側は次のように書きます。

1
2
3
4
# app/views/products/_form.html.haml
# ↓ こんな感じでコードを修正
= simple_form_for @product do |f|
  = f.input :image, as: :image_preview, input_html: { preview_version: :thumb }

すると、アップロード済のデータを編集する際に、formでpreview画像が表示されるようになります。

(3) アップロード画像のサイズのvalidationを行う

アップロードする画像のサイズをチェックするバリデーション(validation)です。今回は幅400px, 高さ400pxより小さい場合にバリデーションでエラーが出るようにします。

まずはバリデーションの記述を追記。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# app/models/product.rb
class Product < ActiveRecord::Base
  # ↓ これらを追記
  # ------------------------------------------------------------------
  # Validations
  # ------------------------------------------------------------------
  validate def check_image_dimensions
    Rails.logger.info "Image upload dimensions: #{geometry[:width]}x#{geometry[:height]}"
    if geometry[:width] < 400 || geometry[:height] < 400
      errors.add :image, '400x400ピクセル以上のサイズの画像をアップロードしてください'
    end
  end

  # ------------------------------------------------------------------
  # Public Instance Methods
  # ------------------------------------------------------------------
  def geometry
    @geometry ||= _geometry
  end

  private
  # ------------------------------------------------------------------
  # Private Instance Methods
  # ------------------------------------------------------------------
  def _geometry
    if image.file and File.exists?(image.file.file)
      img = ::Magick::Image::read(image.file.file).first
      { width: img.columns, height: img.rows }
    end
  end
end

これで、アップロードした画像が幅400px, 高さ400pxより小さい場合はエラーが出るようになります。

Special Thanks

変更来歴

(2014-07-05 21:55) 新規作成
(2014-07-06 09:30) 記述修正
(2016-01-31 09:50) 4.2.5.1対応

おすすめの書籍