まず初めに
ActiveStorageを使って画像を管理していました。
提出する前はいける!と思っていたのですが、欠点を見つけてもらいまして
その不具合というのが
画像ファイル以外のものを投稿し、variant(resize_to_limit: [width, height])
とこのようにリサイズができない時に出るエラーです。
エラー名: ActiveStorage::InvariableError
もっと詳しくいうとActiveStorageが画像の変数
(今回の場合:画像のサイズや形状)を変更できないときに発生するエラーです。
あれ?なぜでしょうしっかりと<%= f.file_field :image, accept: "image/*" %>
この様にしっかりと画像以外のファイルは投稿できないようにフィルターをかけていました。
本当に流石でした。フィルターかけても画像ファイル以外の物を投稿できてしまうのですね。。
解決策
クライアント側だけでなくサーバーサイド側でもバリデーション(検証)をつける
まずは、has_oneまたはmany_attachedで関連付けているモデルに
画像以外のファイルは受け付けないようにカスタムバリデーションを記述しファイルを投稿する前に
検証するようにする。
# frozen_string_literal: true
class Product < ApplicationRecord
has_one_attached :image
validate :image_type
private
# 画像投稿時のバリデーション
def image_type
if image.attached? == false
errors.add(:image, 'を選択してください')
elsif !image.blob.content_type.start_with? 'image/'
errors.add(:image, '以外の種類のファイルは投稿できません!拡張子が.jpg,.jpeg,.png,.gif,.bmp.tiff,.svg,.ico,.webpである必要があります。')
end
end
end
エラーメッセージの部分が変なのはja.yml
で自分でimageを画像と日本語化しているからです
なのでerrors.add(:image, 'を選択してください')
これは画像を選択してください
と
エラーメッセージが出せるようになります。
image_typeメソッドの中身
image.attached?でファイルが選択されているかいないかチェックをします。
falseなので選択されていない状態です。
よってここでDBのNOTNULLの制約と同じことができます。
次に!image.blob.content_type.start_with?
この部分は
ファイルが画像ファイルでない時に起こすことができるエラーメッセージです。
深掘りするとRubyの文字列メソッドであるstart_with?
を使用して、
Active Storageのblobのcontent_type
が指定した文字列で始まるかどうかをチェック!
image.blob.content_type
はActive Storageに保存されたファイル
(この場合、image
)のMIMEタイプを返す。
よってアップロードされたファイルが画像ファイルかどうかをチェックしていて
trueになれば画像ファイルだということを意味しています。
今回は画像ファイルでない時にエラーメッセージが出て登録できないようにするので
!をつけて反転しています。
そして最後にvalidate :image_type
と設定することで自分で作ったカスタムバリデーションが設定
できるということになります!
次にビュー側で一番最初にvariantメソッドを呼び出す時にチェックする必要がある!
私の場合は、編集画面でプレビューする時です
<% if product.image.blob.content_type.start_with? 'image/' %>
<%= image_tag(product.image.variant(resize_to_limit: [200, 200]) %>
<% end %>
これらを設定すると画像を選択するときに画像ファイル以外は選択できないフィルターをかけ
万が一投稿されても、サーバー側でも検証をしているため保存する前に
バリデーションエラーを表示することができます!!