ぺんぎんのRails日記

ぺんぎん。エンジニア経験ゼロ。Rails勉強中。

【Rails】Carrierwaveで画像アップロード

こんにちは。ぺんぎんです。

今回はユーザー登録や投稿の時に使う、画像アップロード機能を実装していきたいと思います。
railsの便利なライブラリ、Carrierwaveを使えば簡単に画像アップロード機能を実装することができます。

Carrierwaveとは

Railsにおける画像アップロード用のgem。
Carrierwaveでは、アップロード機能用のアップローダークラスを作って、細かい設定を書いていきます。
そのため個別のモデルに依存しないで、柔軟にいろいろな設定をすることができます。

手順

1. gemをインストール

Gemfile

gem 'carrierwave'
fem 'minimagick'

今回は画像をリサイズできるようにminimagickもインストールします。 minimagickにはImageMagickが必要になります。 インストール方法はこちら

ターミナル

bundle install

bundle installしたらサーバーを再起動させます。

2. アップローダーを生成する

$ bundle exec rails g uploader アップローダー名
$ bundle exec rails g uploader Image

→app/uploaders/image_uploader.rbというファイルが生成されます。

3. 生成されたアップローダーに設定を記述する

4. ローカル環境でアップロードした画像をアップロードしないように設定する

保存した画像はgithubなどにあげて共有する必要はないですよね。
そこで、.gitignoreファイルに画像の保存先を指定して、プッシュされないように設定しましょう。
.gitignore

/public/uploads

この設定をしないでプッシュてしまった人はこちら

5. boardsテーブルに画像のカラムを追加する

progateで学んだ人もいると思いますが、画像はDBに直接保存するわけではありません。
DBの容量がパンクしないように、画像名のカラムを作って保存するようにします。

$ bundle exec rails g migration AddImageToBoards image:string
$ bundle exec rails db:migrate

これでboardsテーブルに画像用のimageカラムができました。

6. モデルでアップローダークラスとカラムの紐付け

先ほど作ったアップローダークラスは、今のままではBoardクラスとバラバラになっているので、Boardモデルでアップローダークラスを紐づけておきましょう。

class モデル名 < ActiveRecord::Base
  mount_uploader [:カラム名], [アップローダークラス]
end
class Board < ApplicationRecord
  mount_uploader :image, ImageUploader
end

ちなみにmountはよく耳にするあの「マウンティング」と同じ言葉です。
もともと「乗る」という意味で「据え付ける」という意味もあります。

7. コントローラでparamsに画像アップロード用カラムの追加

BoardコントローラではBoard.newで新しい掲示板を作っています。
paramsで受け取れるように、画像用カラムを追加します。

def create
    @board = Board.new(board_params)
end

def board_params
  params.require(:board).permit(:title, :body, :image)
end

8. Viewに画像ファイルのフィールドを追加する

  • 投稿フォーム 画像ファイルを送信する際のファイル選択ボックスを生成します。
    ラベル部分はあとでi18nで日本語を定義します。
<%= f.label :カラム名 %>
<%= f.file_field :保存されるカラム %>

app/views/boards/_form.html.erb

<%= form_with model: board, local: true do |f| %>
  <%= render 'shared/error_messages', object: f.object %>

  <div class="form-group">
    <%= f.label :image %>
    <%= f.file_field :image, class: 'form-control mb-3', accept: 'image/*' %>
    <%= f.hidden_field :board_image_cache %>
  </div>

  <%= f.submit class: 'btn btn-primary' %>
<% end %>

accept属性で選択できるファイルの種類を指定します。
これを指定しないと選択画面でどんなファイルも選択できてしまいます。
「image/*」は画像ファイル全般を指します。

accept属性

また<%= f.hidden_field :board_image_cache %>を入れておくと、バリデーションエラーになった後でも、データが引き継がれ情報を再入力する手間が省けます。

【Rails】CarrierwaveのCache機能を使用し、バリデーション後の画像データを保持する方法

  • 画像表示 アップロードした画像を表示させます。
    image_tagの使い方
<%= image_tag 'ファイル名', 'オプション' %>

ここではファイル名の代わりに、アップローダークラスのurlメソッドを使って、ファイルのURLを取得するようにします。

app/views/boards/_board.html.erb

<%= image_tag board.image_url, class: 'card-img-top', size: '300x200' %>

画像がない場合にデフォルト画像を表示させたい人はこちらを見てください。

【Rails】image_tagを使って簡単に画像を表示させよう!

9. i18nで翻訳を定義する

・imageカラムの日本語を定義します。
config/locales/activerecord/ja.yml

ja:
  activerecord:
    attributes:
      board:
        title: 'タイトル'
        body: '本文'
        image: 'サムネイル'

・画像に関するメッセージの日本語を定義する
Carrierwaveですでに用意されているバリデーションのエラーメッセージを日本語にします。
Carrierwave用の定義ファイルを用意しましょう。

config/locales/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}以下のサイズにしてください"

いろいろな設定

アップローダークラスでいろいろな設定をしましょう。

* アップロードしたファイルの保存場所

アップロードされた画像の保存場所は、デフォルトで「public/uploads/モデル名/画像カラム名/id」に設定されています。

class ImageUploader < CarrierWave::Uploader::Base
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end
end

今回の場合は「public/uploads/board/image/id」に保存されます。
この「id」には掲示板のidが入るので、board.idが1の掲示板で画像をアップロードしたら、「public/uploads/board/image/1」というフォルダに保存されます。

* 画像がない場合のデフォルト画像の指定

画像がないときにデフォルト画像を表示するように設定します。

class ImageUploader < CarrierWave::Uploader::Base
  def default_url
    'sample.png'
  end
end

これを設定しておけば、画像がない時とある時でView側で条件分岐をしなくてすみます。

* アップロードできる拡張子の指定

def extension_whitelist
    %w(jpg jpeg gif png)
  end

gitignoreを設定をしないでgitでコミットしてしまった場合

コミット後にこれを追記しても、アップロード済みのファイルは変更されません。 コミット後はファイル名を指定してgitの管理対象外に変更します。

git rm --chached ファイル名

また不要なファイルをpushしてしまった場合は、リモートリポジトリから削除します。

git rm ファイル名

必ずpush前に不要なファイルがないか確認しましょう( ^ω^ )

参照

CarrierWave github.com

Rails】CarrierWaveチュートリアル pikawaka.com