Rails 掲示板の画像アップロード機能の実装 手順 (自分用)
#アップロード用のライブラリ(carrierwave)をインストール
gem 'carrierwave'
・carrierwaveをgemに追加して、bundle install
$ bundle install
#アップローダーを生成
$ bundle exec rails g uploader アップローダー名
$ bundle exec rails g uploader BoardImage
コマンドを実行すると、app/uploaders/board_image_uploader.rbファイルが作成される。
###生成されたアップローダー
class BoardImageUploader < CarrierWave::Uploader::Base
storage :file #アップロードファイルの保存場所を指定
def store_dir #アップロードファイルの保存するディレクトリを指定
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def default_url #デフォルトでっ表示する画像を指定。「app/assets/images」に保存した写真が使える。(最初は、sample.jpgが入っている)
'board_placeholder.png'
end
def extension_whitelist #アップロードするファイルの許可する拡張子を指定
%w(jpg jpeg gif png)
end
end
・デフォルトでstorage :fileが指定されているので、アップロードしたファイルはpublic/配下に保存される。保存されるディレクトリは、store_dirで設定されるので、アップロードしたファイルは、「public/uploads」に保存される。
#アップロード画像のカラムを追加
・アップロード画像の情報を保存するboard_imageカラムをboardsテーブル追加する為に、マイグレーションファイルを作成
$ bundle exec rails g migration AddBoardImageToBoards board_image:string
$ bundle exec rails db:migrate
・マイグレーションを実行して、boardsテーブルにboard_imageカラムを追加。
#アップローダークラスとカラムの紐づけ
class モデル名 < ActiveRecord::Base
mount_uploader [:カラム名], [アップローダークラス]
end
・投稿の際に画像も投稿する機能を追加したいので、boardモデルに先ほど作成した投稿画像用の「board_imageカラム」と「BoardImageUploaderクラス」を紐づける。
class Board < ApplicationRecord
mount_uploader :board_image, BoardImageUploader #追記
belongs_to :user
validates :title, presence: true, length: { maximum: 255 }
validates :body, presence: true, length: { maximum: 65_535 }
end
・boardモデルで画像を投稿する際の「board_imageカラム」の設定を「BoardImageUploaderクラス」で行えるようになる。
#boardコントローラーに設定しているストロングパラメーターに受け取るカラムを追加
def board_params
params.require(:board).permit(:title, :body, :board_image, :board_image_cache)
end
・「board_image」「board_image_cache」を追加。
<%= 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, 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' %>
</div> #ここまで
<%= f.submit class: 'btn btn-primary' %>
<% end %>
<%= f.label :board_image %>
・「board_image」カラムの入力項目表示。jaファイルで名前変更可能。
<%= f.file_field :board_image, class: 'form-control mb-3', accept: 'image/*' %>
・「form.htmlタグ名 :カラム名」と指定する。カラム名は保存される先のテーブルのカラム名を指定するので、「board_image」を指定。これで、boardテーブルのboard_imageカラムに投稿した画像のデータが送られる。
・acceptでファイルの種類を指定。image/*は画像ファイル全般。例えば、video/*は動画ファイル全般。
<%= f.hidden_field :board_image_cache %>
・バリデーションに引っかかり、掲示板が登録できなかった時(エラーが出て入力画面に戻された時)に選んだ画像を保持しておく機能。(入力画面に戻された時に再度画像を選び直さなくていいように。)
#掲示板の部分テンプレートに、アップロードした画像のURLを指定
<div class="col-sm-12 col-lg-4 mb-3">
<div id="board-id-<%= board.id %>">
<div class="card">
<%= image_tag board.board_image_url, class: 'card-img-top', size: '300x200' %> #追記
<div class="card-body">
<h4 class="card-title">
<a href="#">
<%= board.title %>
</a>
</h4>
<div class='mr10 float-right'>
<a href="#"><%= icon 'fas', 'trash', class: 'pr-1' %></a>
<a href="#"><%= icon 'fa', 'pen' %></a>
</div>
<ul class="list-inline">
<li class="list-inline-item">
<%= icon 'far', 'user' %>
<%= board.user.decorate.full_name %>
</li>
<li class="list-inline-item">
<%= icon 'far', 'calendar' %>
<%= l board.created_at, format: :long %>
</li>
</ul>
<p class="card-text"><%= board.body %></p>
</div>
</div>
</div>
</div>
<%= image_tag board.board_image.url, class:'card-img-top', size:'300x200' %>
・保存した画像(board_image)を表示させるには、画像が保存されている場所(パス)を取得する必要がある
・Boardモデルに、BoardImageUploaderクラスとboard_imageカラムを紐づけたことで「url」メソッドが使えるようになっている。
・「urlメソッド」 → ファイルのURLを取得 「使い方の例:boardモデル.board_image.url」
・「board.board_image.url」でBoardモデルのboard_imageカラムからurlを取得している。
*アップローダーでデフォルトで表示される画像を設定しているので、画像が設定されていないとういう事が起きないため、if,elseでの画像が設定されていなかったらなどの分岐は必要ない。
.gitignoreファイルの設定
・public/uploads/配下に保存される画像は、Githubなどにアップロードする必要がないので、以下のように.gitignoreファイルに/public/uploadsを指定してGit管理下から除外
/public/uploads