掲示板に画像アップロード機能を実装
実装内容
- 掲示板作成画面から画像を投稿する。
- gem CarrierWaveとMiniMagickを利用。
- サムネイルというラベルを追加し、画像の選択をする。
- 画像を選択せずに投稿した場合、デフォルトの画像を表示。
- アップロードできるファイルはjpg, jpeg, png, gifのみに制限。
- 画像投稿時にプレビューとして表示。
導入
CarrierWaveはRailsアプリケーションに画像アップロード機能をつけることができる。
MiniMagickはアップロードされた画像のリサイズなどができる。
MiniMagickを使用するには、ImageMagickが必要なので事前にインストールする。
$ brew install imagemagick
gem 'carrierwave' gem 'mini_magick'
Boardモデルのboardsテーブルにboard_imageカラムを追加する。
board_imageカラムには画像情報(ファイルそのものではない)が格納される。
$ rails g migration AddBoardImageToBoards board_image:string Running via Spring preloader in process 10390 invoke active_record create db/migrate/20210520113137_add_board_image_to_boards.rb
画像を選択せずに掲示板を作成できるようにしたいので、マイグレーションファイルはそのままでNOTNULL制約はつけない
class AddBoardImageToBoards < ActiveRecord::Migration[5.2] def change add_column :boards, :board_image, :string end end
$ rails db:migrate == 20210520113137 AddBoardImageToBoards: migrating ============================ -- add_column(:boards, :board_image, :string) -> 0.0013s == 20210520113137 AddBoardImageToBoards: migrated (0.0016s) ===================
画像uploaderの作成
ジェネレータでuploaderを生成することから始める。
$ rails g uploader image(アップローダー名)
これにより次のファイルが生成される。
ここには画像のリサイズなど、設定を記述する。
create app/uploaders/image_uploader.rb
uploaderとモデルを紐付ける。
今回はBoardモデルと紐付ける。
class Board < ApplicationRecord mount_uploader :board_image, ImageUploader
image_uploader.rbの編集
このファイルはデフォルトでコードが記述されており、コメントアウトを解除して使用する。 MiniMagickでリサイズするので記述。
class ImageUploader < CarrierWave::Uploader::Base include CarrierWave::MiniMagick
デフォルトの設定
storage :file
public/uploads/
に画像が保存される。def store_dir
画像が保存される先のpathを指定。
storage :file def store_dir "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" end
追加設定
def default_url 'board_placeholder.png' # app/assets/images/board_placeholder.png end def extension_allowlist %w(jpg jpeg gif png) end
viewの作成
掲示板作成フォームを編集する。
画像投稿時にプレビューとして表示するには、画像はまだデータベースへ登録されていないので、選択した画像はjavascriptを使って表示させる必要がある。
#... <div class="form-group"> <%= f.label :board_image %> <%= f.file_field :board_image, onchange: 'previewFile(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' %> </div> #...
onchange
イベントハンドラでjavascriptを指定フォームの入力値、選択が変更されたときに処理を行う。
accept
でファイルを指定する。image/は画像ファイル、video/*は動画ファイル。
<%= f.hidden_field :board_image_cache %>
これは、掲示板作成できなかった場合、アップロードした画像を消えないようにする処理。
javascriptでプレビューを表示させる。
jsファイルの作成。
application.jsは個別のjavascriptを設定するファイルなので、ここには記述してはいけない。
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 = ''; } }
掲示板一覧画面へアップロードした画像を表示させる。
board.rb
のboard_imageからurlをとってくる。
<%= image_tag board.board_image.url, class: 'card-img-top', size: '300x200' %>
ストロングパラメータに画像のフィールドを追加。
privatedef board_params params.require(:board).permit(:title, :body, :board_image, :board_image_cache) end end
ローカルでアップロードした画像をリモートへアップロードしないように設定。
.gitignoreに画像のアップロード先を追加する。
# Ignore vendor /public/uploads
rmコマンドでリモートリポジトリから削除する。
$ git rm -r --cached public/uploads
# public/uploadsディレクトリ下のファイルをすべて削除