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


始めに...

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

環境

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

手順

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

Gemをインストール

Gemfile
gem 'carrierwave'
gem 'carrierwave-aws'
$ bundle install

Deviseにカラムを追加

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を追加

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

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ファイルの作成

$ rails g uploader avatar

アンコメントアウトした部分のみ表示しています。

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」はカラム名。

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

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

S3の設定

こちらの記事を参考に設定してください。
「パブリックアクセスの設定」は記事と同じ部分をオフにしないとエラーが起きます。
ブログの画像をamazon s3に保存する
Railsでaction text(active storage)の画像保存先をaws s3に変更した

CarrierWaveファイルの作成

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

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

アバターの保存と表示

アバター保存画面

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 %>

アバター表示画面


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

後書き

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

参考

railsでcarrierwaveを使って画像をアップロード、表示
ブログの画像をamazon s3に保存する
Railsでaction text(active storage)の画像保存先をaws s3に変更した
carrierwave