すでにメンバーの場合は

無料会員登録

GitHubアカウントで登録 Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

登録がまだの方はこちらから

Pikawakaにログイン

GitHubアカウントでログイン Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

CarrierWaveでファイルアップロード機能を実装しよう

この記事で出来るようになること

はじめに

概要

この記事では、CarrierWaveという強力なgemを用いてファイルアップロード機能を実装する方法について学びます。CarrierWaveを使うことで、Railsアプリケーションに画像や他のファイルをアップロードする機能を簡単かつ効率的に追加できます。本記事では、ユーザー詳細ページの実装も行います。

目標

この章の目標
  • CarrierWave gemの導入と設定方法を理解し、Railsアプリケーションに適用する。
  • 画像ファイルのアップロード、保存、表示のための基本的な流れを習得する。
  • Userモデルに画像アップロード機能を統合し、学習メモ一覧、ユーザー詳細ページでの画像表示を実現する。

事前準備

データベースを作り直しましょう

まずは、以下のコマンドを実行して、データベースをリセットしておきましょう。

ターミナル(~/environment/memo_app) | データベースを作り直す
1
rails db:migrate:reset
アプリケーションをブラウザで確認しましょう

Railsサーバーを起動したら、Webブラウザを使用してアプリケーションのルートページにアクセスします。そこからヘッダーにある「アカウント登録」リンクをクリックして、アカウント登録画面を表示しましょう。

ユーザーを新規登録しましょう

以下の情報でユーザーネーム、メールアドレス、パスワード、パスワードの確認を入力フォームに記入し、登録するボタンをクリックしてユーザーを新規登録しましょう。

  • ユーザーネーム:ありす
  • メールアドレス:hoge@example.com
  • パスワード:000000
  • パスワードの確認:パスワードを入力

アカウント登録後に学習メモを作成しましょう

アカウント登録が完了したら、ログイン状態で学習メモを新規作成してみましょう。「新規作成」リンクをクリックして学習メモの投稿フォームにアクセスします。

以下の内容で学習メモを作成しましょう。

  • タイトル:URLを構成する基本的な要素
  • 本文:URLの基本的な構成要素には、プロトコル(Scheme)、ドメイン名(Host)、そしてパス(Path)の3つがある。

学習メモを投稿した後、ありすさんの学習メモが一覧に追加されて表示されることを確認しましょう。

これで事前準備は全て完了しました。ありすさんのユーザーでログインした状態で、次に進みましょう。

ぴかわかさん

ファイルアップロード機能の概要

ファイルアップロード機能とは

ファイルアップロード機能は、ユーザーがアプリケーションに画像や文書などのファイルをアップロードできるようにする機能です。ユーザーは自分のデバイスからファイルを選択し、アプリケーションに送信することができます。

例えば、「ファイルを選択」ボタンなどをクリックすると、ファイルエクスプローラー(FinderやWindowsエクスプローラーなど)が開き、ユーザーはアップロードしたいファイルを選んでアプリケーションに送信できます。

このようなファイルアップロード機能により、ユーザーはアプリケーション内でプロフィール画像を設定したり、商品画像をアップロードしたりすることができます。

ぴっかちゃん

ファイルアップロード機能があれば、ユーザーがアプリケーション内で直接ファイルを共有し、多様なコンテンツを作り出せるのがいいね!

アプリケーションによっては、画像に限らず、文書や音声ファイルなど、さまざまな種類のファイルアップロードをサポートする場合があるよ。本章では、画像ファイルのアップロード機能を実装するよ!

ぴかわかさん

画像ファイルの管理方法

ファイルアップロード機能で、特に画像ファイルをうまく扱うことは、アプリケーションの使いやすさやデータの整理にとても重要です。画像ファイルを管理する際に最も大切なのは、データベースに直接画像を保存しないということです。

画像をデータベースに保存すると、次のような問題が起きる可能性があります。

  1. データベースのサイズが大きくなる: 画像や動画はファイルサイズが大きいので、これらをデータベースに入れると、データベースのサイズが膨大になりすぎて、アプリの動きが遅くなることがあります。

  2. バックアップや復元が難しくなる: データベースのサイズが大きくなると、バックアップや復元が複雑になり、時間もかかります。

  3. データベースの動作が遅くなる: たくさんの画像データがデータベースに入っていると、データを読み書きする速度が遅くなり、アプリ全体の速度に影響します。

そのため、通常は画像ファイルをデータベースに保存するのではなく、アプリケーションサーバー上など(※1)の特定のディレクトリに保存します。そして、データベースには、その画像ファイルへの参照情報(例えば、ファイルパスやファイル名)のみを保存します。

開発環境時のアプリケーションサーバー上に保存される例

画像ファイルへの参照情報でファイル名がデータベースに保存される例

この方法により、画像ファイルを効率的に管理し、アプリケーションのパフォーマンスを維持できます。Railsでは「CarrierWave」というgemを利用することで、ファイルアップロード機能を簡単に実装することができます。

ポイント

画像ファイルは、アプリケーションサーバー上の特定のディレクトリに保存し、データベースには、その画像の参照情報のみを保存する

ファイルアップロード機能の基本実装

学習メモアプリケーションに「CarrierWave」を活用して、画像ファイルのアップロード機能を実装します。CarrierWaveを使用することで、画像ファイルのアップロード、保存、表示などの処理が容易になります。

具体的には、アカウント登録時にユーザーが自身のアバター画像を「任意」でアップロードできる機能を追加します。また、ユーザーが後から自分のアバターを編集できるようにすることも含めます。

現在、ユーザーが投稿した学習メモのヘッダーにはデフォルトのアイコンが表示されていますが、ユーザーがアバターを登録している場合はそのアバターを表示し、登録していない場合はデフォルトのアイコンを表示させます。

それでは、手を動かしながら画像ファイルアップロード機能を実装していきましょう!

ぴかわかさん

1.CarrierWaveの導入

CarrierWaveを導入しよう

Gemfileに以下の1行を追加し、ターミナルでアプリケーションのルートディレクトリに移動して、bundle installコマンドを実行し、CarrierWaveをインストールしましょう。

Gemfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.7.5'

gem 'rails', '~> 6.1.3', '>= 6.1.3.1'
gem 'mysql2', '0.5.3'
gem 'puma', '5.5.2'
gem 'sass-rails', '6.0.0'
gem 'webpacker', '5.4.3'
gem 'turbolinks', '5.2.1'
gem 'jbuilder', '2.11.5'
gem 'bootsnap', '1.10.1', require: false
gem 'net-http'
gem 'devise', '4.9.3'
gem 'devise-i18n', '1.12.0'
gem 'carrierwave', '~> 2.0'
group :development, :test do gem 'byebug', '11.1.3' gem 'pry-rails', '0.3.9' end group :development do gem 'web-console', '4.2.0' # gem 'rack-mini-profiler', '2.3.3' gem 'listen', '3.7.1' gem 'spring', '3.1.1' end group :test do gem 'capybara', '3.36.0' gem 'selenium-webdriver', '4.1.0' gem 'webdrivers', '5.0.0' end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

Gemfileに追加した後は、ターミナルで以下を実行しましょう。

ターミナル(~/environment/memo_app)
1
bundle install

サーバーを起動している場合は、再起動しておきましょう。

2.実装手順の確認

ユーザーがアカウントを登録または編集する際にアバターを追加できるようにするためには、まずUserモデルにアバター属性を追加する必要があります。Userモデルにはdeviseが適用されているため、以前のユーザーネームの追加手順と同様に、Applicationコントローラ内でのパラメータ許可の設定を行います。

アバターは任意の属性であるため、Userモデルでのバリデーションやエラーメッセージの設定は不要です。さらに、UserモデルでCarrierWaveの画像アップロード機能を利用できるように設定を追加する必要があります。実装手順は以下の通りです。

  1. マイグレーションファイルの作成: Userモデルにアバターのカラムを追加するためのマイグレーションファイルを生成します。

  2. マイグレーションの実行: 生成したマイグレーションファイルを使ってデータベースにアバターのカラムを追加します。

  3. ビューファイルの編集: 新規登録やアカウント情報編集のフォームにアバター画像をアップロードするためのフィールドを追加します。

  4. コントローラーでのパラメータ設定: Application コントローラーで、アバター画像に関連するパラメータをストロングパラメータに設定します。

  5. モデルにアップローダーのマウント: UserモデルにCarrierWaveのアップローダーをマウントします。これにより、アバター画像のアップロード機能がUserモデルに組み込まれます。

  6. 学習メモのヘッダー表示の調整: 学習メモのヘッダーにおいて、ユーザーがアバターを登録している場合はそのアバターを表示し、登録していない場合はデフォルトのアイコンを表示させます。

それでは実際に手を動かしながら、Userモデルにアバター属性を追加して、画像アップロード機能を実装してみましょう。

ぴかわかさん

3.画像アップロード機能の実装

avatarカラムの追加

新しい属性をUserモデルに追加するには、最初にマイグレーションファイルを作成して、データベースに新しいカラムを追加する必要があります。ユーザー情報を管理するusersテーブルにavatarという新しいカラムを追加します。

見やすさを考慮してavatarカラムをusernameカラムの左側に移動しています。

avatarカラムには、ユーザーがアップロードしたアバター画像の「ファイル名」が保存される予定です。そのため、avatarカラムのデータ型はstringと指定します。

マイグレーションファイルを生成して、データベースに適用しましょう

ターミナルで以下のコマンドを実行し、マイグレーションファイルを生成し、データベースに変更を適用しましょう。

ターミナル(~/environment/memo_app)
1
2
rails g migration AddAvatarToUsers avatar:string
rails db:migrate

マイグレーションを実行した後、phpMyAdminを使用してusersテーブルを確認すると、avatarカラムが追加されていることを確認できます。

実際のカラムの並び順はこの通りである必要はありません。

アバター項目をフォームへ追加

ユーザーが新規登録やアカウント情報を編集する際、フォームにアバターの項目を含める必要があります。このアバター項目は、新規登録とアカウント編集の際にのみ必要であり、ログインフォームには不要です。

そのため、以下の新規登録用と編集用ビューファイルにアバター項目を追加します。

  • ユーザー新規登録画面: app/views/devise/registrations/new.html.erb
  • ユーザー情報編集画面: app/views/devise/registrations/edit.html.erb
フォームにアバターの項目を追加しましょう

アカウント登録と編集のビューファイルにアバターの項目を追加しましょう。アバターの追加にあわせて、適切なスタイリングも行います。

app/views/devise/registrations/new.html.erb | 新規登録フォームにアバターを追加する
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<h2>アカウント登録</h2>

<%= form_with model: resource, as: resource_name, url: registration_path(resource_name), local: true, class: 'form' do |f| %>
  <%= render 'shared/validation_error', instance: resource %>

  <div class="form__group">
    <%= f.label :username, 'ユーザーネーム(必須):' %>
    <%= f.text_field :username, autofocus: true, autocomplete: 'username', class: 'form__input' %>
  </div>

  <div class="form__group">
    <%= f.label :email, 'メールアドレス(必須):' %>
    <%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form__input' %>
  </div>

<div class="form__group">
<%= f.label :avatar, 'アバター(任意):' %>
<%= f.file_field :avatar, class: 'form__file' %>
</div>
<div class="form__group"> <%= f.label :password, 'パスワード(必須):' %> <%= f.password_field :password, autocomplete: 'new-password', placeholder: '6文字以上で入力してください', class: 'form__input' %> </div> <div class="form__group"> <%= f.label :password_confirmation, 'パスワード確認(必須):' %> <%= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form__input' %> </div> <div class="form__submit"> <%= f.submit '登録する', class: 'form__button' %> </div> <% end %>
app/views/devise/registrations/edit.html.erb | 編集フォームにアバターを追加する
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<h2>マイページ編集</h2>

<%= form_with model: resource, url: registration_path(resource_name), local: true, method: :put, class: 'form' do |f| %>
  <%= render 'shared/validation_error', instance: resource %>

  <div class="form__group">
    <%= f.label :username, 'ユーザーネーム(必須):' %>
    <%= f.text_field :username, autofocus: true, autocomplete: 'username', class: 'form__input' %>
  </div>

  <div class="form__group">
    <%= f.label :email, 'メールアドレス(必須):' %>
    <%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form__input' %>
  </div>

<div class="form__group">
<%= f.label :avatar, 'アバター(任意):' %>
<%= f.file_field :avatar, class: 'form__file' %>
</div>
<div class="form__group"> <%= f.label :password, 'パスワード:' %> <%= f.password_field :password, autocomplete: 'new-password', placeholder: '空欄のままなら変更しません', class: 'form__input' %> </div> <div class="form__group"> <%= f.label :password_confirmation, 'パスワード確認:' %> <%= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form__input' %> </div> <div class="form__group"> <%= f.label :current_password, '現在のパスワード(必須):' %> <%= f.password_field :current_password, autocomplete: 'current-password', placeholder: '変更を反映するには現在のパスワードを入力してください', class: 'form__input' %> </div> <div class="form__submit"> <%= f.submit '更新する', class: 'form__button' %> </div> <% end %>

layout.scssファイルを開いて、以下の2箇所にスタイルを追加しましょう。

app/assets/stylesheets/layout.scss | 追加する箇所
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// ...省略...
.form {
  margin: 40px 0 25px;

  &__group {
    margin-bottom: 20px;
display: flex;
flex-direction: column;
align-items: flex-start;
} &__input, &__textarea { width: 100%; height: 42px; border: 1px solid #ddd; border-radius: 2px; padding: 10px 16px; margin: 8px 0; &:focus-visible { outline: -webkit-focus-ring-color auto 1px; } &.form__textarea { height: 240px; } }
&__file {
margin: 8px 0;
}
&__submit { text-align: center; padding-top: 15px; font-weight: 700; margin-bottom: 40px; } &__button { border-radius: 4px; padding: 10px 30px; text-align: center; } } // ...省略...
アカウント登録と編集フォームにアバターが追加されていることを確認しましょう

Webブラウザでアプリケーションのルートページにアクセスしましょう。現在はありすさんとしてログインしているため、ルートページは以下のように表示されます。次に、ヘッダーにある「マイページ編集」リンクをクリックして、アカウント編集画面に進みます。

アカウント編集画面には、アバターを追加するための新しい項目がフォームに含まれていることがわかります。

ありすさんのユーザーからログアウトしましょう。

その後、「アカウント登録」リンクをクリックしてアカウント登録画面を表示してください。そうすると、以下のようにアバターの項目が追加されているのが確認できます。

現段階では、「ファイルを選択」ボタンをクリックして画像をアップロードしても、コントローラーでのパラメータ許可やモデルでのCarrierWave設定が完了していないので、アバターはまだ登録できません。次に実際にアバター画像を登録できるか確かめてみます。

新規ユーザーを登録しましょう

以下の情報で登録フォームに必要情報を入力し、ユーザーを新規登録してみてください。アバター画像は、リンクからダウンロードしてアップロードを試みてください。

  • ユーザーネーム:たけし
  • メールアドレス:fuga@example.com
  • アバター:こちらの画像(user_sample_avatar.png)をダウンロードしてください。
  • パスワード:111111
  • パスワードの確認:パスワードを入力

以下のように「ファイルを選択」をクリックして画像をアップロードすると、「選択されてません」という表示が画像のファイル名user_sample_avatar.pngに変わります。必要な情報を入力し終えたら、「登録する」ボタンをクリックしてください。

ユーザーを新規登録した後、以下のようにルートページにリダイレクトされ、成功のメッセージが表示されます。しかし、実際にはアバターは登録されていません。

アバターが登録されているかを確認しましょう

phpMyAdminでusersテーブルのavatarカラムを確認しましょう。

ここでは、たけしさんのアバター画像のuser_sample_avatar.pngが保存されていることを期待しますが、実際には以下のようにNULLとなっていることがわかります。

このようにusersテーブルにavatarカラムを追加し、ビューフォームにアバターの入力欄を設けただけでは画像が保存されないことが確認できました。画像をデータベースに正しく保存するためには、さらにいくつかのステップを完了する必要があります。

まず、Userモデルにユーザーネームを追加した時と同様に、Applicationコントローラで画像データを許可するための設定を追加しましょう。

ぴかわかさん

Applicationコントローラでパラメータ設定

ユーザーネームを追加した際に学んだ内容ですが、少し振り返ります。

deviseを使ったUserモデルでは、メールアドレスやパスワードなどのデフォルト属性は特別な設定なしで使えます。しかし、新しく追加した属性は、Applicationコントローラ内で明示的に許可する設定が必要です。許可にはconfigure_permitted_parametersメソッドを利用します。

configure_permitted_parametersメソッドの基本書式
1
2
3
4
5
6
class ApplicationController < ActionController::Base

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:アクション名, keys: [:カラム名1, :カラム名2, ...])
  end
end

上記で指定したアクションでの処理中に、keys:で指定したカラムのパラメータが許可され、データベースに保存されます。

Applicationコントローラでアバター属性を許可しましょう

今回は、ユーザーが新規登録(sign_up)とアカウント情報を更新(account_update)する際に、アバターがデータベースに保存されるようにします。

application_controller.rbファイルを開くと、すでに新規登録と更新でユーザーネームの設定が行われていることを確認できます。こちらにアバターも追加していきます。

app/controllers/application_controller.rb | 変更前
1
2
3
4
5
6
7
8
class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
devise_parameter_sanitizer.permit(:account_update, keys: [:username])
end end

以下のように:usernameの後にカンマで区切り、 :avatar を追加しましょう。

app/controllers/application_controller.rb | 変更後
1
2
3
4
5
6
7
8
class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:username, :avatar])
devise_parameter_sanitizer.permit(:account_update, keys: [:username, :avatar])
end end
マイページ編集でアバター画像を更新しましょう

マイページ編集画面で「ファイルを選択」をクリックし、user_sample_avatar.pngをアップロードします。そして、「現在のパスワード」欄に111111を入力し、「更新する」ボタンをクリックしてアバター画像を更新しましょう。

ユーザーを更新した後、以下のようにルートページにリダイレクトされ、成功のメッセージが表示されます。しかし、実際にはデータベースにアバター画像のファイル名は登録されていない状態です。

phpMyAdminでusersテーブルのavatarカラムを確認しましょう。

先ほどビューファイルにアバターの項目を追加し、ユーザー登録を行った際の確認で、usersテーブルのavatarカラムはNULLとなっていました。

今回のApplicationコントローラでのパラメータ許可設定を行った後、マイページ編集画面でuser_sample_avatar.pngをアップロードして更新したため、avatarカラムにはそのファイル名が保存されていることが期待されます。

しかし、phpMyAdminでusersテーブルのavatarカラムを確認すると、以下のように画像のファイル名ではなく、「ActionDispatch::Http::UploadedFile」というインスタンスのメタデータが文字列として保存されていることが確認できます。

ActionDispatch::Http::UploadedFileのインスタンスはアップロードされたファイルの一時情報を保持していますが、これを直接データベースに保存すると、ファイル自体ではなくインスタンスのメタデータが文字列として保存されてしまいます。これではファイルを適切に管理やアクセスすることが困難になります。

次にCarrierWaveの機能を使用可能にして、アップロードされたファイルを効果的に管理できるように適切な設定を行う必要があります。

ぴかわかさん

UserモデルにCarrierWaveのアップローダーをマウント

CarrierWaveを利用すると、画像ファイルのアップロード、保存、表示などを簡単に行うことができます。この機能を使用するためには、CarrierWaveのアップローダークラスを生成し、対象のモデルにアップローダークラスをマウントする(関連付ける)必要があります。

CarrierWaveのアップローダークラスには、アップロードされたファイルに対して行うさまざまな処理がデフォルトで定義されています。

CarrierWaveのアップローダークラスを生成しましょう

CarrierWaveのアップローダークラスを生成するには、rails g uploaderコマンドを使用します。このコマンドを実行することにより、ファイルアップロード機能に必要な設定を含んだクラスファイルがアプリケーション内に作成されます。

今回は、画像ファイルのアップロードに特化した設定を行うアップローダークラスが必要なので、コマンドにImageという名前を指定して実行しましょう。

ターミナル(~/environment/memo_app)| 画像ファイルに関する設定を行うアップローダークラスを生成
1
rails g uploader Image

上記を実行すると、画像アップロードに関連する設定ファイルが生成されます。

アップローダークラスのデフォルト設定を見てみましょう

app/uploaders/image_uploader.rbファイルを開きましょう。

生成されたImageUploaderクラス内には、以下のようにデフォルトで設定されている項目がいくつかあります。

設定項目の内容は、以下の通りです。

  • storage :file - アップロードされたファイルの保存先を「アプリケーションのサーバー上のファイルシステム」に指定。
  • store_dirメソッド - アップロードされたファイルが保存されるディレクトリのパスを定義。デフォルトでは、ファイルはuploadsディレクトリ下の、アップローダーがマウントされたモデルのクラス名とIDに基づいたパスに保存される。

これらの設定は、次にUserモデル内の特定のカラムにマウントする際にそのまま利用します。設定に基づいた実際の画像ファイルの保存先は、あとでアップロード操作を実行した際に確認します。

アップローダーをUserモデルにマウントしましょう

CarrierWaveで提供されているmount_uploaderメソッドを使用することで、アップローダークラスをモデル内の特定の属性(カラム)に関連付けることができます。これにより、そのカラムに画像やファイルアップロード機能が追加されます。

モデルクラス内 | mount_uploaderメソッドの使い方
1
mount_uploader :カラム名, 関連付けたいアップローダークラス名

今回であれば、Userモデル内で以下を定義することで、avatar属性にアップロードされたファイルがImageUploaderクラスで定義された方法にしたがって処理されるようになります。

app/models/user.rbファイルを開いて、以下の1行を追加しましょう。

Userモデルのクラス内に定義する
1
mount_uploader :avatar, ImageUploader
ぴっかちゃん

これで設定されたディレクトリにユーザーのアバター画像が保存され、ファイル名がデータベースに正しく登録されたり更新されたりするようになるね!

次は、画像がどこに保存されるかと、データベースには本当にファイル名が保存されるかを確認していこう!

ぴかわかさん
マイページ編集でアバター画像を更新しましょう

マイページ編集画面で「ファイルを選択」をクリックし、user_sample_avatar.pngをアップロードします。そして、「現在のパスワード」欄に111111を入力し、「更新する」ボタンをクリックしてアバター画像を更新しましょう。

ユーザーを更新した後、以下のようにルートページにリダイレクトされ、成功のメッセージが表示されます。今回はUserモデルのavatar属性に画像アップロード機能を使えるように設定してあるので、正常に保存されているはずです。

phpMyAdminでusersテーブルのavatarカラムを確認しましょう。

phpMyAdminを使用して、たけしさんのアバター画像のファイル名がavatarカラムに正しく保存されているか確認しましょう。

以下のようにuser_sample_avatar.pngが格納されているはずです。

アップロードされた画像ファイルの保存先を確認しましょう

ImageUploaderクラスがUserモデルのavatar属性にマウントされているため、avatarにアップロードされた画像ファイルはImageUploaderクラスに定義されている設定に従って処理されます。

アップロードされた画像ファイルの具体的な保存先はImageUploaderクラス内のstore_dirメソッド内で定義されています。

store_dirメソッドのデフォルト設定は、アップローダークラスがマウントされたモデルの情報と属性に基づいて画像ファイルの保存先ディレクトリを定義します。

この設定により、アップロードされた画像はモデル名、マウントされた属性名、およびモデルのIDを含む構造化されたディレクトリパスに保存されるようになります。このパスの最上位の階層は、Railsアプリケーションのpublicディレクトリ内に位置します。

今回であれば、ユーザーがアバター画像をアップロードした場合、その画像ファイルはpublicディレクトリ内のuploads/user/avatar/{user_id}ディレクトリ内に保存されます。ここで{user_id}はそのユーザーのidに置き換えられます。

たけしさんのid2なので、以下のディレクトリ内に保存されています。

このようにアップロードされた画像ファイルは、アップローダークラスの設定に基づいたディレクトリ内に保存され、データベースのavatarカラムにはファイル名が保存されます。

次に、CarrierWaveを利用して保存された画像をビューに表示する方法について学びます。

CarrierWaveで画像をビューに表示する方法

ビューで画像を表示するためには、CarrierWaveが提供するurlメソッドとヘルパーメソッドーのimage_tagを組み合わせて使用します。

まずurlメソッドは、CarrierWaveによってアップローダークラスがモデルの属性にマウントされた際に使用できます。このメソッドは、アップロードされた画像ファイルへの具体的なURLパスを取得することが可能になります。

例えば、以下のようにuser変数がたけしさんのUserインスタンスを保持している場合、user.avatar.urlを呼び出すことで、たけしさんのアバター画像ファイルの保存場所へのURLを取得できます。

CarrierWaveは、アップロードされたファイルを管理するために内部的にパスを生成し、それをurlメソッドを通じて提供します。このURLは、Railsアプリケーション内のpublicディレクトリからの相対パスです。

上記の場合は、以下のようにimage_tagメソッドの第一引数に指定することで、ビュー内で画像を表示することができます。

例:たけしさんのアバター画像を表示する場合
1
<%= image_tag user.avatar.url, alt: 'ユーザーアイコン' %>

それでは、このあと実際にアバター画像を表示できるように変更していきます。

ぴかわかさん
メモ投稿者のアバター画像のパスを取得する方法を理解しましょう

現在、ユーザーが投稿した学習メモのヘッダーには、以下のようにデフォルトのアイコンが表示されています。

この表示はapp/views/memos/_memo.html.erbファイルでimage_tagメソッドを使用してuser_icon.pngを表示させています。

今度は、ユーザーがアバターを設定している場合は、そのアバターを表示し、未設定の場合はデフォルトのアイコンを表示するように変更しましょう。

部分テンプレート_memo.html.erb内でのアバター画像のパスの取得方法を理解するために少し振り返ります。まず、部分テンプレート内のmemoにはindex.html.erbから各学習メモのインスタンスが渡されます。

このmemoオブジェクトを通じて、関連づけられたユーザーの情報を取得することができます。例えばmemo.userを使うと、そのメモに紐付いたユーザーの情報を取得でき、さらにmemo.user.usernameを使うと、該当ユーザーのユーザーネームを取得できます。

これは、Memoモデル内に定義されているbelongs_to :userによって、メモが特定のユーザーに関連づけられているためです。

そのため、メモを投稿したユーザーのアバター画像にはmemo.user.avatar.urlを使用してアクセスすることができます。

_memo.html.erb内でメモ投稿者のアバター画像を表示する場合
1
<%= image_tag memo.user.avatar.url, alt: 'ユーザーのアイコン' %>
部分テンプレート_memo.html.erbファイルを編集しましょう

メモ投稿者がアバターを設定している場合、そのアバター画像を表示し、アバターが未設定の場合はデフォルトのアイコンを表示したい場合は、以下のように条件分岐を使います。

app/views/memos/_memo.html.erbファイルを開いて、ユーザーアイコンの箇所を変更しましょう。

app/views/memos/_memo.html.erb | 変更内容
1
2
3
4
5
<% if memo.user.avatar.present? %>
   <%= image_tag memo.user.avatar.url, alt: 'ユーザーのアバター' %>
<% else %>
   <%= image_tag 'user_icon', alt: 'ユーザーアイコン' %>
<% end %>

たけしさんのアバター画像の表示を確認しましょう

まず、学習メモを新規作成してみましょう。たけしさんでログインしている状態です。「新規作成」リンクをクリックして学習メモの投稿フォームにアクセスします。

以下の内容で学習メモを作成しましょう。

タイトル:プロトコルについて
本文:プロトコルは、インターネット上で情報をやり取りするときの「取り決め」や「ルール」を指す。URLの先頭にある「https」(または「http」)がこれに該当する。これはWebサイトとの情報のやり取りにhttpsというプロトコルを使用することを示す。

学習メモを投稿した後、たけしの学習メモが一覧に追加されて、アバター画像が表示されることが確認できます。

学習メモを作成してみましょう

次に進む前に、たけしさんの学習メモをもう1つを作成しましょう。
以下の内容で学習メモを作成しましょう。

タイトル:ドメイン名について
本文:ドメイン名は、Webサイトの名前やアドレスのようなもので、インターネット上の特定の場所(サーバーの位置)を示す。

特定のファイル形式のみを許可する設定

アバター画像としてアップロードを許可するのは画像ファイルのみに限定する必要があります。現状では、任意のファイルタイプがアップロード可能となってしまい、例えばsample.txtのような非画像ファイルもアップロードできてしまいます。

実際に動作を体験したい方は、こちらリンクからファイル(sample.txt)をダウンロードして試してみましょう。

アバターに非画像ファイルがアップロードされる例

非画像ファイルが登録されてしまうと、以下のように画面が崩れてしまいます。

この問題を解決するため、Userモデルのavatar属性にマウントされるImageUploaderクラスを編集し、特定の画像ファイル形式のみを許可するように設定します。

app/uploaders/image_uploader.rbファイルを開いて、以下の38行目からのextension_allowlistメソッドのコメントを解除し、%w(jpg jpeg png)に変更して、許可するファイル形式を制限しましょう。

この変更により、jpgjpegpngの拡張子を持つファイルのみがアップロードできるようになり、その他のファイル形式は許可されません。

この段階では、エラーメッセージが英語で表示されるため、「Translation missing」というメッセージが表示されている部分があります。次に日本語化を行います。

エラーメッセージを日本語化するために翻訳ファイルを編集しましょう。config/locales/ja.ymlファイルを開き、適切な場所にハイライト内容を追加してください。

config/locales/ja.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
ja:
  errors:
    format: "%{attribute}%{message}"
messages:
extension_whitelist_error: "は許可されている拡張子(jpg, jpeg, png)のみアップロード可能です。"
activerecord: attributes: memo: title: "タイトル" content: "内容" user: email: "メールアドレス" password: "パスワード" password_confirmation: "パスワード確認" username: "ユーザーネーム"
avatar: "アバター"
errors: models: memo: attributes: title: blank: "を入力してください。" too_long: "は30文字以内で入力してください。" content: blank: "を入力してください。" user: attributes: email: blank: "を入力してください。" password: blank: "を入力してください。" taken: "は既に使用されています。" blank: "が入力されていません。" too_short: "は%{count}文字以上に設定して下さい。" too_long: "は%{count}文字以下に設定して下さい。" invalid: "は有効でありません。" confirmation: "が内容とあっていません。" password_confirmation: blank: "を入力してください。" taken: "は既に使用されています。" blank: "が入力されていません。" too_short: "は%{count}文字以上に設定して下さい。" too_long: "は%{count}文字以下に設定して下さい。" invalid: "は有効でありません。" confirmation: "がパスワードと一致しません。" current_password: blank: "を入力してください。" username: blank: "を入力してください。"

これで、アバター画像としてjpgjpegpngの拡張子を持つファイルのみがアップロード可能になり、その他のファイル形式は拒否されるように設定されました。また、不適切なファイル形式でアップロードを試みた場合には、日本語でエラーメッセージが表示されるようになります。

動作を確認した方は、たけしさんのアバター画像をuser_sample_avatar.pngに戻して次に進みましょう。

ぴかわかさん

ユーザー詳細ページの実装

最後に、学習メモ一覧画面で、各投稿者のユーザーネームにリンクを追加し、そのユーザーが投稿した学習メモの一覧を表示するユーザー詳細ページに遷移するように実装します。

ユーザー詳細のルーティングを設定しましょう

まず、ルーティングを設定しましょう。
ユーザーの詳細ページにアクセスするためのルーティングが必要です。config/routes.rbファイルを開き、ユーザー詳細ページ用のルーティングを追加します。そのためには、以下のようにresources :users, only: :showの行をファイルに追加してください。

config/routes.rb
1
2
3
4
5
6
Rails.application.routes.draw do
  root 'memos#index'
  devise_for :users
  resources :memos
resources :users, only: :show
end
ユーザー詳細用のコントローラを設定しましょう

次に、ユーザー詳細用のコントローラを設定します。まだusersコントローラが存在しないので、次のコマンドでusersコントローラを生成してください。

ターミナル(~/environment/memo_app)
1
rails g controller users

その後、app/controllers/users_controller.rbファイルを開いて、showアクションを設定します。加えて、ユーザーの情報を取得するためのプライベートメソッドset_userを定義し、before_actionでshowアクション実行前にこれを呼び出します。

app/controllers/users_controller.rb
1
2
3
4
5
6
7
8
9
10
11
12
class UsersController < ApplicationController
  before_action :set_user, only: :show

  def show
  end

  private

  def set_user
    @user = User.find(params[:id])
  end
end
Userモデルに関連付けを追加しましょう

Userモデルにメモとの関連付けを定義しましょう。app/models/user.rbファイルを開き、Userモデルにメモとの1対多の関連付けを追加します。

以下のようにhas_many :memosという行を追加することで、1つのユーザーが複数のメモを持つことができるようになります。これにより、例えば@user.memosを使って、特定のユーザーに紐づくすべてのメモ情報を取得することが可能になります。

app/models/user.rbファイルにhas_many :memosを追加しましょう。

詳細ビューを作成しましょう

app/views/usersディレクトリ内にshow.html.erbファイルを作成しましょう。

次にshow.html.erbファイルを開き、以下のコードを追加して、ユーザーが持つ学習メモ一覧を表示しましょう。

app/views/users/show.html.erb
1
2
3
<h2><%=@user.username %>さんの学習メモ一覧</h2>

<%= render @user.memos %>

renderメソッドに@user.memosを渡すことで、_memo.html.erb部分テンプレートを活用して、関連付けられたメモを一覧で表示できます。

投稿者のメモ詳細へのリンクを設定しましょう

app/views/memos/_memo.html.erbファイル内で、ユーザー名を表示している部分をlink_toメソッドを使用してリンクに変更します。これにより、ユーザーネームをクリックすると、そのユーザーの詳細ページへ遷移できるようになります。

app/views/memos/_memo.html.erb | 変更前
1
<span class="memo__username"><%= memo.user.username %></span>

上記を以下に変更しましょう。

app/views/memos/_memo.html.erb | 変更後
1
<span class="memo__username"><%= link_to memo.user.username, user_path(memo.user) %></span>
学習メモ一覧画面から各ユーザーの詳細ページへアクセスしてみましょう

最後に、学習メモ一覧画面で各ユーザーネームが表示される部分に設定したリンクを確認します。ユーザーネームのリンクをクリックすることで、そのユーザーが投稿した学習メモの一覧が表示される詳細ページに遷移するか確認しましょう。

「たけし」のリンクをクリックした後、たけしさんが投稿した学習メモの一覧が表示されれば、リンク設定は完了です。

以下の画面に遷移するはずです。

これでCarrierWaveを利用したファイルアップロード機能とユーザー詳細の実装は完了です!お疲れ様でした!

ぴかわかさん