carrier_waveとmini_magickを使った掲示板の画像アップロード機能の作成

どうもどハマりエンジニアです。

本日は掲示板の画像アップロード機能の作成を行ったよ。

あくまで自分で確認するためと、アウトプットして覚えるためようです。

 

carrierwaveのgemをインストール

Gemfile
 
gem carrierwave

bundle installを行う。

 uploaderを生成

ターミナル
 
rails g uploader board_image

「この設定をこのモデルとこのモデルに適応する」といったことを柔軟に行えるようにする。

uploader
 
def store_dir
  "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end

carrierwaveを通じた画像のアップロード先をどこにするのかを指定しており、指定されたディレクトリに、アップロードされたファイルが保存される。

DBに保存されるのは、ファイルそのものではなく、ファイルへの参照データ

 

$ rails g db:migrate AddBoard_imageToBoard Board_image:string

マイグレーション も作成しておく。

 モデルの関連付け

boardモデルと、boardカラムに保存される画像の関連付けを行います。

board.rb
 
class Board < ApplicationRecord
  belongs_to :user
  mount_uploader :board_image, BoardImageUploader
  validates :title, presence: true, length: { maximum: 255 }
  validates :body, presence: true, length: { maximum: 65_535 }
end

 uploadファイルでの設定

uploard
 
class BoardImageUploader < CarrierWave::Uploader::Base
  storage :file

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  def default_url
    'board_placeholder.png'
  end

  def extension_whitelist
    %w[jpg jpeg gif png]
  end
end

デフォルトでの画像を指定。

アップロードできる拡張子を制限。

 formを作成

formをパーシャルで作成

<%= form_with model: @board, local: true do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="form-group">
<%= f.label :title %>
<%= f.text_field :title, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :body %>
<%= f.text_area :body, class: 'form-control', rows: 10 %>
</div>
<div class="form-group">
<%= f.label :board_image %>
<%= f.file_field :board_image, onchange: 'previewFileWithId(preview)',
          class: 'form-control mb-3', accept: 'image/*' %>
<%= f.hidden_field :@board_image_cache %>
</div>
<div class='mt-3 mb-3'>
<%= image_tag @board.board_image.url,
id: 'preview',
size: '300x200' %>
<%= f.submit class: 'btn btn-primary' %>
</div>
</div>
<% end %>

 

ここでの、accept: 'image/*は、画像ファイル全般を指定。

 
<%= image_tag board.board_image.url, id: 'preview', size: '300x200' %>

プレビューを表示。boardモデルのboard_imageのurlを呼び出し表示。

 
<%= f.hidden_field :board_image_cache %>

hidden属性でimage_cacheは、画像を指定したけれども、バリデーションエラーなどにより保存が失敗した場合の画面再表示時などに、画像情報をキャッシュしておくための領域。

 
<%= f.file_field :board_image, class: "form-control",
 accept: 'image/*',onchange:'previewFileWithId(preview)' %>

onchangeとは、イベントハンドラープロパティで、ユーザーの入力の応じて動的に表示内容を変えるときに発生するchangeイベントを処理。

input,select,textarea要素において、ユーザーによる要素の変更が完了した際に行われる。

onchangeの書き方は、

 
onchange: '関数名()'

で表す。

 JSファイルを設定する

javascriptの記載は、application.jsには記載せず、別の専用ファイルを作成する。

ここでは、application.jsは個別のJavaScriptを読み込む専用のファイルのため、preview.jsファイルを作成し記載する。

preiew.js
 
function previewFileWithId(id) {
    const target = this.event.target;
    const file = target.files[0];
    const reader  = new FileReader();
    reader.onloadend = function () {
        preview.src = reader.result;
    }
    if (file) {
        reader.readAsDataURL(file);
    } else {
        preview.src = '';
    }
}

1行ずつ解説していくと、

 
const target = this.event.target;

は、

traget・・・操作を差し込む対象のオブジェクト
event.target・・・最初にイベントが起こった要素です。今回だと、ファイルを選択した時のイベントを示す。

 
const file = target.files[0];

targetには「files」というプロパティが用意されている。
管理されているファイルは、「File」というオブジェクトの形をしている。
このFileオブジェクトには、ファイルに関する各種の情報や、ファイルアクセスのためのメソッドなどがまとめられている。

files[0]で一つ目のFileオブジェクトを取り出す。

 
const reader  = new FileReader();

FileReaderオブジェクトで、ファイルを取得。

 
reader.onloadend = function () {
        preview.src = reader.result;
    }

onloadend、FileReaderのイベント。データの読み込みが成功か失敗に関わらず終了した時にloadendイベントが発生し、ここに設定したコールバック関数が呼び出される。

そして、取得されたファイルの結果を、previewのsrc属性に指定。

 
if (file) {
        reader.readAsDataURL(file);
    } else {
        preview.src = '';
    }

readAsDataURL()は、FileReaderのメソッドです。ファイルを、Data URIとして読み込むメソッド。例えば画像ファイルをこのメソッドで読み込んで、読み込んだデータをimg要素のsrc属性に指定すればブラウザに表示できる。

 エラーメッセージを設定

最後にエラーメッセージを設定。

carrierwave.ja.ymlを新たに生成し、記載していく。

carrierwave.ja.yml
 
ja:
  errors:
    messages:
      carrierwave_processing_error: 処理できませんでした
      carrierwave_integrity_error: は許可されていないファイルタイプです
      carrierwave_download_error: はダウンロードできません
      extension_whitelist_error: "は %{allowed_types}の形式でアップロードしてください"
      extension_blacklist_error: "%{extension}ファイルのアップロードは許可されていません。アップロードできないファイルタイプ: %{prohibited_types}"
      content_type_whitelist_error: "%{content_type}ファイルのアップロードは許可されていません。アップロードできるファイルタイプ: %{allowed_types}"
      content_type_blacklist_error: "%{content_type}ファイルのアップロードは許可されていません"
      rmagick_processing_error: "rmagickがファイルを処理できませんでした。画像を確認してください。エラーメッセージ: %{e}"
      mini_magick_processing_error: "MiniMagickがファイルを処理できませんでした。画像を確認してください。エラーメッセージ: %{e}"
      min_size_error: "を%{min_size}以上のサイズにしてください"
      max_size_error: "を%{max_size}以下のサイズにしてください"

これで完成!