Rails
carrierwave

【日本初?】CarrierWaveでアップロードされた画像の横幅などをValidateする方法

More than 5 years have passed since last update.

弊社ブログ記事からの転載です
CarrierWaveでアップロードされた画像の横幅などをValidateする方法 @ heathrow.lab

最近CarriweWaveのことばかり書いてる気もしますが、気にせず今回もCarrierWaveです。

例えば横幅100px未満の画像がアップロードされた場合、エラーにしたいってことありますよね。
CarrierWaveにデフォルトでそういうValidatorが提供されていればいいんですが、そうでもないのが悩ましい。

では、どういった方法でやればいいのか。
日本語情報がなかったようのなので、弊社で行っている方法を共有してみます。
# Rmagick使います

下準備

1.画像の横幅縦幅を取得するためCarrierWave::RMagickを拡張
2.画像の横幅縦幅を検証するValidatorクラスを作成

まず、CarrierWave::RMagicを拡張します。
このset_dimensionという関数で
アップロードされた画像をRmagickで読み込んで、横幅縦幅を@dimension保存させます。

config/initializers/carrierwave.rb
module CarrierWave
  module RMagick
    attr_reader :dimension

    def set_dimension
      image = Magick::Image.read(current_path).shift
      @dimension = { width: image.columns, height: image.rows }
    end
  end
end

次に、Validatorの作成です。
先ほどの#set_dimensionで保存した横幅縦幅を検証します。

app/validators/image_size_validator.rb
class ImageSizeValidator < ActiveModel::EachValidator
  OPTIONS = {
    width: {
      field:    :width,
      function: :'==',
      message:  'invalid_image_width'
    },
    max_width: {
      field:    :width,
      function: :'<=',
      message:  'invalid_image_max_width'
    },
    min_width: {
      field:    :width,
      function: :'>=',
      message:  'invalid_image_min_width'
    },
    height: {
      field:    :height,
      function: :'==',
      message:  'invalid_image_height'
    },
    max_height: {
      field:    :height,
      function: :'<=',
      message:  'invalid_image_max_height'
    },
    min_height: {
      field:    :height,
      function: :'>=',
      message:  'invalid_image_min_height'
    }
  }.freeze

  def validate_each(object, attribute, value)
    return if value.nil? || value.dimension.nil?

    dimension = value.dimension
    options.each do |key, val|
      next unless OPTIONS.key? key
      check = OPTIONS[key]
      unless dimension[check[:field]].send check[:function], val
        object.errors[attribute] << (options[:message] || I18n.t("activerecord.errors.messages.#{check[:message]%[val]}"))
      end
    end
  end
end

使い方

Uploaderに対して#set_dimensionを登録してやります。

app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
  include CarrierWave::RMagick

  process :set_dimension
end

Modelでこんな風にかけます

app/models/photo.rb
class Photo < ActiveRecord::Base
  mount_uploader :image1, ImageUploader
  mount_uploader :image2, ImageUploader

  # 横幅640px、縦幅920pxでなければならない
  validates :image1, presence: true, image_size: { width: 640, height: 920 }

  # 横幅が100px以上でなければならない
  validates :image2, presence: true, image_size: { min_width: 100 }
end

弊社ブログ記事からの転載です
CarrierWaveでアップロードされた画像の横幅などをValidateする方法 @ heathrow.lab