はじめに
ShrineはRailsでのアップロードを簡単に行えるようにするGemです。
詳しくはこちらを御覧ください。
Shrineで画像をアップロードする処理を実装するなら、多くの場合モデルに関連付けて使用すると思います。
しかし、今回は下記の理由によりモデルに関連付けずに使用しました(ファイル名自体はDBに保存しています)
- モデルに関連付けて使用する場合テーブルにtext型のxxx_dataというカラムが必要になる。
- (S3の署名付きURLなどで)画像の公開範囲を制限したいのでShrineが提供しているメソッドがそのままでは使えない。
実装の際、アップロード時のリサイズやバリデーションなどに少しハマったので対応方法を記載します。
アップロード時にリサイズする
まず、アップロードに関する処理ですが、他のアップロード処理にも使いまわせる用にモジュール化しています。
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | 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です。
次に、モデルに関連付ける場合のアップローダのコード例は下記のとおりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 |
上記のコードではアップロード時にリサイズ処理が実行されません。
モデルに関連付けない場合は下記の用になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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を追加しました。
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 | 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 |
アップロード時に古いファイルを削除する
モデルに関連付ける場合、アップロードし直すと古い画像が削除されますが、関連付けない場合画像がどんどん増えていきます。
下記の方法でアップロード時に古いファイルを削除するようにしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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でアップロードする際にハマったポイントと対応について紹介しました。