改【Railsチュートリアル】プロフィール画像をアプリ内で設定できるように変更

#はじめに
前回の記事[【Railsチュートリアル】プロフィール画像をアプリ内で設定できるように変更](https://qiita.com/supyolo888/items/def08450cc4190977324)で、sample_appにおいて、プロフィール画像をアプリ内で設定できるようにしたが、本番環境でアセットパイプラインに関してのエラーが発生したため、carrierwaveを用いた方法に変更する。
また、[Rialsチュートリアル第6版13章](https://railstutorial.jp/chapters/user_microposts?version=6.0#sec-image_upload_in_production)においてAWSのS3を作成済みなので、今回は本番環境のみそちらにアップロードするよう設定していく。


**前提**
第6版sample_appが完成している

#carrierwaveの設定

Gemfileにcarrierwaveを追加

```rb:Gemfile
gem 'carrierwave'
```

```
$ bundle install
```

アップローダーの作成し、uploadersディレクトリの中にavatar_uploader.rbが作成されているのを確認する。

```
$ rails g uploader Avatar
```

usersテーブルにavatarカラムを追加する。
**ここで注意すべきなのが、カラム名をimageにしてしまうと何故かエラーが発生するのでそれ以外にする必要がある。**

```
rails g migration add_avatar_to_users avatar:string
```

```rb:db/migrate/[timestamop]_add_avatar_to_users.rb
class AddImageToUsers < ActiveRecord::Migration[6.1]
  def change
    add_column :users, :avatar, :string
  end
end
```

```
$ rails db:migrate
```
ユーザーコントローラーのuser_paramsにavatarカラムを追加

```rb:app/controllers/users_controller.rb
.
.
.
  private

    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation, :avatar)
    end
.
.
.
```
ユーザーモデルにマウントを記述

```app/models/user.rb
class User < ApplicationRecord
.
.
.
  mount_uploader :avatar, AvatarUploader
.
.
.
```

#ユーザー編集画面で画像変更を可能にする

初期画像default_user.jpgをapp/assets/imagesディレクトリ下に用意しておく。

```
├ app/
    ├ assets/
           ├ images/
　　　　　　　　　　　　　　　　　　　　        └ default_user.jpg
```

ユーザー編集のビューを変更する。

```rb:app/views/users/edit.html.erb
    .
    .
    .
    <%= form_with(model: @user, local: true) do |f| %>
    .
    .
    .
      <% if @user.avatar? %>
        <%= image_tag @user.avatar.url, width: "100px", height: "100px", class:"icon"%>
      <% else%>
        <%= image_tag "default_user.jpeg", width: "100px", height: "100px", class:"icon"%>
      <% end %>
      
      <%= f.label :avatar, "Icon change" %>
      <%= f.file_field :avatar, class: 'form-control' %>

      <%= f.submit "Save changes", class: "btn btn-primary" %>
    <% end %>

  </div>
</div>
```

以下のようになっていれば成功。

![スクリーンショット 2021-11-23 21.07.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2200191/71069914-e66d-dcc1-9d49-b488b972ca7c.png)

#画像を表示する

gravatarで表示していたビューをimage_tagに変更していく。
画像を持たないユーザーにはデフォルトの画像を表示させる。
編集するビューは以下。

- views/microposts/micropost.html.erb
- views/shared/_user_info.html.erb
- views/users/_user.html.erb
- views/users/edit.html.erb
- views/users/show_follow.html.erb
- views/users/show.html.erb

例えば

```rb:views/microposts/micropost.html.erb
<%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
```

を

```rb:views/microposts/micropost.html.erb
<% if micropost.user.avatar? %>
  <%= link_to image_tag(micropost.user.avatar.url, width: "50px", height: "50px", class:"icon"),micropost.user%>
<% else%>
  <%= link_to image_tag("default_user.jpeg", width: "50px", height: "50px", class:"icon"),micropost.user%>
<% end %>
```

というように変更する。

最後にcssを変更する。
自分は画像を丸く表示させたかったので以下のように記述。

```rb:app/assets/stylesheets/custom.scss
.
.
.
.icon {
  border-radius: 50%;
  object-fit: cover;
  float: left;
  margin-right: 10px;
}
```

以下のようになっていれば成功。

![スクリーンショット 2021-11-23 21.31.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2200191/70362c68-5649-a605-5912-2025a99c83d7.png)

#本番環境でAWS S3にアップロードするよう設定

現在の設定ではアップロードされた画像はpublic/uploads/user/avatarに追加されていくが、本番環境のみ[Rialsチュートリアル第6版13章](https://railstutorial.jp/chapters/user_microposts?version=6.0#sec-image_upload_in_production)で作成したAWS S3にアップロードするように設定していく。

Gemfileにfog-awsを追加

```rb:Gemfile
gem 'fog-aws'
```

```
$ bundle install
```

avatar_uploader.rbの設定を変更
今回は画像のみ受け付けたいので`def extension_allowlist`も記述したが、iPhoneで撮影した写真は拡張子がheicであることも多いため、jpgなどに変換する機能を検討中である。

```app/uploaders/avatar_uploader.rb
.
.
.
  if Rails.env.development?
    storage :file
  elsif Rails.env.test?
    storage :file
  else
    storage :fog
  end
.
.
.
  def extension_allowlist
    %w(jpg jpeg png)
  end
end
```

チュートリアルにおいて、下記の環境変数をHerokuに設定済みなので、そちらを利用していく。
![スクリーンショット 2021-11-24 20.20.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2200191/f90c02ed-e6cd-d157-4e1f-21078f84d4d6.png)
もしまだなら、

```
$ heroku config:add
```

で追加していく。

config/initializers配下に、carrierwave.rbを作成し、設定を記述していく。

```config/initializers/carrierwave.rb
require 'carrierwave/storage/abstract'
require 'carrierwave/storage/file'
require 'carrierwave/storage/fog'
if Rails.env.production?
  CarrierWave.configure do |config|
    config.fog_provider = 'fog/aws'
    config.fog_directory = ENV['AWS_BUCKET']
    config.fog_credentials = {
      provider: 'AWS',
      region: ENV['AWS_REGION'],
      aws_access_key_id: ENV['AWS_ACCESS_KEY'],
      aws_secret_access_key: ENV['AWS_SECRET_KEY'],
      path_style: true
    }
  end
end
```

AWSを開き、Amazon S3→バケット→アクセス許可と進み、ブロックパブリックアクセス (バケット設定)を以下のように設定する。
![スクリーンショット 2021-11-25 0.52.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2200191/d1e78858-48a8-47dc-7184-1f4c01499544.png)

バケットポリシーに以下のように記述

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "statement1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::(AWSアカウントID):user/(IAMユーザー名)"
            },
            "Action": "*",
            "Resource": "arn:aws:s3:::(バケット名)"
        }
    ]
}
```