ShrineはRailsでのアップロードを簡単に行えるようにするGemです。
詳しくはこちらを御覧ください。
Shrineで画像をアップロードする処理を実装するなら、多くの場合モデルに関連付けて使用すると思います。
しかし、今回は下記の理由によりモデルに関連付けずに使用しました(ファイル名自体はDBに保存しています)
実装の際、アップロード時のリサイズやバリデーションなどに少しハマったので対応方法を記載します。
まず、アップロードに関する処理ですが、他のアップロード処理にも使いまわせる用にモジュール化しています。
module FileUploadable extend ActiveSupport::Concern def save_after_upload(image_params) if image_params[:image].blank? return self.save end content_type = image_params[:image].content_type if content_type.include?('jpeg') extension = '.jpg' elsif content_type.include?('png') extension = '.png' else return end remove_images_if_existed # 画像のファイル名をDBに保存する self.image_filename = "#{Time.now.to_i}#{extension}" return unless upload_images(image_params) self.save end def image_path "#{base_path}/#{self.image_filename}" end def thumb_image_path filename = self.image_filename.split('.').first extension = self.image_filename.split('.').last "#{base_path}/#{filename}_thumb.#{extension}" end private def upload_images(image_params) return false unless image_params[:image].present? # XxxImageUploaderという名前のアップローダのインスタンスを生成する uploader_class = "#{self.class.name}ImageUploader".constantize uploader = uploader_class.new(:store) # 大サイズの画像をアップロードする upload_file = uploader.upload(image_params[:image], location: image_path) return false unless upload_file.present? # アップロードした画像のバリデーションを行う attacher = uploader_class::Attacher.new(self, :image) attacher.assign(upload_file) return false if attacher.errors.present? # サムネイル画像をアップロードする upload_thumb_file = uploader.upload(image_params[:image], location: thumb_image_path, versions: 'thumb') upload_thumb_file.present? end def remove_images_if_existed return unless self.image_filename.present? if Rails.env.production? # TODO: S3から削除する処理を実装する else FileUtils.rm_rf("./public/images/uploads/store/#{base_path}") end end def base_path "#{self.user_id}/#{self.class.name.underscore}" end end
実装のアップロードは「upload_images」で行っており、引数はStrong Parametersです。
次に、モデルに関連付ける場合のアップローダのコード例は下記のとおりです。
require 'image_processing/mini_magick' class CoachImageUploader < Shrine include ImageProcessing::MiniMagick plugin :processing plugin :versions plugin :delete_raw plugin :validation_helpers plugin :determine_mime_type process(:store) do |io, context| # リサイズを行う large = resize_to_limit!(io.download, 400, 400) { |cmd| cmd.auto_orient } thumb = resize_to_limit(large, 150, 150) { large: large, thumb: thumb } end # 省略 end
上記のコードではアップロード時にリサイズ処理が実行されません。
モデルに関連付けない場合は下記の用になります。
require 'image_processing/mini_magick' class CoachImageUploader < Shrine # 省略 def process(io, context) if context[:versions].blank? or context[:versions] == :original resize_to_limit(io, 800, 800) { |cmd| cmd.auto_orient } else resize_to_limit(io, 300, 300) { |cmd| cmd.auto_orient } end end # 省略 end
バリデーションを行うためにモデルにアタッチする必要があったので、やむなくモデルに「image_data」というattributeを追加しました。
module FileUploadable extend ActiveSupport::Concern # 省略 private def upload_images(image_params) return false unless image_params[:image].present? # XxxImageUploaderという名前のアップローダのインスタンスを生成する uploader_class = "#{self.class.name}ImageUploader".constantize uploader = uploader_class.new(:store) # 大サイズの画像をアップロードする upload_file = uploader.upload(image_params[:image], location: image_path) return false unless upload_file.present? # アップロードした画像のバリデーションを行う attacher = uploader_class::Attacher.new(self, :image) attacher.assign(upload_file) return false if attacher.errors.present? # サムネイル画像をアップロードする upload_thumb_file = uploader.upload(image_params[:image], location: thumb_image_path, versions: 'thumb') upload_thumb_file.present? end # 省略 end
モデルに関連付ける場合、アップロードし直すと古い画像が削除されますが、関連付けない場合画像がどんどん増えていきます。
下記の方法でアップロード時に古いファイルを削除するようにしました。
module FileUploadable extend ActiveSupport::Concern # 省略 def remove_images_if_existed return unless self.image_filename.present? if Rails.env.production? # TODO: S3から削除する処理を実装する else FileUtils.rm_rf("./public/images/uploads/store/#{base_path}") end end # 省略 end
モデルに関連付けずにShrineでアップロードする際にハマったポイントと対応について紹介しました。