【Rails】Devise、CarrierWave、S3でユーザーアバターの実装

# 始めに...
CarrierWaveでユーザーアバターを実装したのですが、S3のパブリックアクセスを一部オフにしなければならなかったので最終的にActiveStorageを使用する方法に切り替えました。
ActiveStorageでの実装記事も作成したので比べてみてから取り掛かるといいかと思います。
[【Rails】Devise、ActiveStorage、S3でユーザーアバターの実装](https://qiita.com/chabudai/items/8305d34995c459e4d2a0)

# 環境
Ruby:  2.7.1
Rails: 6.0.2.2
Devise導入済み
(gem 'devise', '~> 4.7', '>= 4.7.1')

# 手順
今回はカラム名を「avatar」で実装してますが好みでimageなどに変更できます。

## Gemをインストール

```ruby:Gemfile
gem 'carrierwave'
gem 'carrierwave-aws'
```

```zsh
$ bundle install
```

## Deviseにカラムを追加

```ruby:db/migrate/××××_devise_create_users.rb
class DeviseCreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      # avatarカラムの追加
      t.string :avatar
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""
# ~略~
end
```

## Deviseのstrong parametersを追加
今回は編集画面でのみアバターを設定できるようにします。

```ruby:app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected
    # deviseのpermitted_parameterを追加する
    def configure_permitted_parameters
      devise_parameter_sanitizer.permit(:account_update, keys: [:avatar] )
      # 登録時も必要であればsign_upを追加
      # devise_parameter_sanitizer.permit(:sign_up, keys: [:avatar] )
    end
end
```

## uploaderファイルの作成

```zsh
$ rails g uploader avatar
```
アンコメントアウトした部分のみ表示しています。

```ruby:app/uploaders/avatar_uploader.rb
class AvatarUploader < CarrierWave::Uploader::Base

  # 本番環境でのみawsへ保存される。
  if Rails.env.production?
    storage :aws
  else
  # 開発環境でテストする場合はこちらもawsに変更してください。
    storage :file
  end

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  # 許可されるファイルの指定（pngなどの追加可）
  def extension_whitelist
    %w(jpg jpeg)
  end

  def filename
    original_filename if original_filename
  end

end
```

## モデルの設定
「AvatarUploader」は先ほどのuploaderファイル名。
「:avatar」はカラム名。

```ruby:app/models/user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  # CarrierWaveで利用するUploader
  mount_uploader :avatar, AvatarUploader
end
```

## S3の設定
こちらの記事を参考に設定してください。
「パブリックアクセスの設定」は記事と同じ部分をオフにしないとエラーが起きます。
[ブログの画像をamazon s3に保存する](https://w-muro.github.io/blog/log/2020/04/29/%E3%83%96%E3%83%AD%E3%82%B0%E3%81%AE%E7%94%BB%E5%83%8F%E3%82%92Amazon-S3%E3%81%AB%E4%BF%9D%E5%AD%98%E3%81%99%E3%82%8B)
[Railsでaction text(active storage)の画像保存先をaws s3に変更した](https://w-muro.github.io/blog/log/2020/05/20/Rails%E3%81%A7Action-Text(Active-Storage)%E3%81%AE%E7%94%BB%E5%83%8F%E4%BF%9D%E5%AD%98%E5%85%88%E3%82%92AWS-S3%E3%81%AB%E5%A4%89%E6%9B%B4%E3%81%97%E3%81%9F)


## CarrierWaveファイルの作成
「config/initializers」に「carrierwave.rb」ファイルを作成して次のコードを書き込んでください。
githubも見たけど作成コマンドは無さそうだったのでそれぞれ作成してください。

```ruby:config/initializers/carrierwave.rb
CarrierWave.configure do |config|
  if Rails.env.production?
    config.storage    = :aws
    config.aws_bucket = "バケット名"
    config.aws_acl    = 'public-read'

    # The maximum period for authenticated_urls is only 7 days.
    config.aws_authenticated_url_expiration = 60 * 60 * 24 * 7

    # Set custom options such agit s cache control to leverage browser caching
    config.aws_attributes = {
        expires: 1.week.from_now.httpdate,
        cache_control: 'max-age=604800'
    }

    # aws credential
    # 次の項目は前項、S3の設定にある記事を参考にしてください
    config.aws_credentials = {
        access_key_id:     Rails.application.credentials.dig(:aws, :access_key_id),
        secret_access_key: Rails.application.credentials.dig(:aws, :secret_access_key),
        region:            'ap-northeast-1'
    }
  else
    # 本番環境以外はローカルにファイルを保存する
    config.storage    = :file
  end
end
```


## アバターの保存と表示
アバター保存画面

```html:app/views/users/registrations/edit.html.erb
<%= form_with model: @user, url: user_registration_path, local: true do |f| %>
  <div class="field">
    <%= f.label :avatar %>
    <%= f.file_field :avatar %>
  </div>

  <div class="actions">
    <%= f.submit "保存" %>
  </div>
<% end %>
```

アバター表示画面

```html

<%= image_tag @user.image.to_s %>
```


# 後書き
実装方法を忘れないためにざっとまとめてみました。
しかし、概要でも書いたのですがActiveStorageのほうが何かと都合がよさそうなので今後CarrierWaveを使うことはないかもしれません。

# 参考
[railsでcarrierwaveを使って画像をアップロード、表示](https://qiita.com/Inp/items/cc447237e23bf10d159e)
[ブログの画像をamazon s3に保存する](https://w-muro.github.io/blog/log/2020/04/29/%E3%83%96%E3%83%AD%E3%82%B0%E3%81%AE%E7%94%BB%E5%83%8F%E3%82%92Amazon-S3%E3%81%AB%E4%BF%9D%E5%AD%98%E3%81%99%E3%82%8B)
[Railsでaction text(active storage)の画像保存先をaws s3に変更した](https://w-muro.github.io/blog/log/2020/05/20/Rails%E3%81%A7Action-Text(Active-Storage)%E3%81%AE%E7%94%BB%E5%83%8F%E4%BF%9D%E5%AD%98%E5%85%88%E3%82%92AWS-S3%E3%81%AB%E5%A4%89%E6%9B%B4%E3%81%97%E3%81%9F)
[carrierwave](https://github.com/carrierwaveuploader/carrierwave)