【Rails】CarrierWaveとrmagickで画像アップロード機能を作る

2018年4月18日

自作のWEBアプリを作成していますが、その中でユーザープロフィール画像のアップロード機能を作っています。

CarrierWaveとimageMagick、rmagickというGemをインストールして行います。

参考サイトを見ながら比較的簡単に導入する事ができました。今日はその流れを解説します。

CarrierWaveをインストールして画像アップロード機能をつける

■Gemfileに以下を追記します。

----------------------------
#画像アップロード
gem 'carrierwave'
----------------------------

■バンドルインストールを実行

$ bundle install

■アップローダーを作成します

$ rails g uploader image
Running via Spring preloader in process 5536
create app/uploaders/image_uploader.rb

■userモデルに関連付けを記述します。
userモデルにはimageというカラムがあり、ここに画像のURLを格納するような仕様になっています。

/models/user.rbに以下を追記。

  #カラムの名前をmount_uploaderに指定
  mount_uploader :image, ImageUploader

■users_controller.rbの編集

userコントローラーのeditアクション(プロフィール編集画面)で、@userインスタンスを生成する記述。

あとストロングパラメーターを記述します。


  def edit
    @user = User.find(params[:id])
  end
  
  #ストロングパラメーター
  def user_params
    params.require(:user).permit(:nickname, :email, :password, :password_confirmation, :image)
  end

このuserモデルには、ニックネーム、メールアドレス、パスワード、画像URLのカラムがありますので、上記のように記述しています。

ユーザープロフィール編集画面からPOSTで送られてくるので、それを受け取る時のストロングパラメーターの設定ですね。

■コントローラーを記述したらビューの編集

ユーザープロフィール編集画面
/user/edit.html.erb

<%= form_for(@user) do |f| %>
  <% if @user.image? %>
    <%= image_tag @user.image.url %>
  <% else %>
    <%= image_tag("/images/icon/no-image.png") %>
  <% end %>
  <%= f.file_field :image %>
  <%= f.submit 'プロフィール編集を完了する', class: 'btn btn-primary' %>
<% end %>

1行目はform_forでフォームタグを生成しています。いつもの書き方です。@userはusers_controllerのeditアクションで生成していてそれを使用します。

2行目で@user.image?で、画像URLがあるかチェックし有れば画像を表示しますが、無ければno-imageを表示しています。で、このプロフィール画像の下に

  <%= f.file_field :image %>

を記述して、これが画像ファイルを選択するボタンになるわけですね。


このフォームタグの最後に、f.submitで「プロフィール編集を完了する」ボタンを用意しています。画像だけ編集するのではなくてユーザー情報の全部を入力し終えて完了という仕様にしています。

ポストで送られてきたデータを処理(画像を保存)する

上記の「プロフィール編集を完了する」ボタンが押下されるとPOSTでデータが送られてきます。

users_controllerのupdateメソッドに来るので、そこを編集します。

■users#updateメソッドの編集

  def update
    @user = User.find(params[:id])

    #編集しようとしてるユーザーがログインユーザーとイコールかをチェック
    if current_user == @user

      if @user.update(user_params)
        flash[:success] = 'ユーザー情報を編集しました。'
        render :edit
      else
        flash.now[:danger] = 'ユーザー情報の編集に失敗しました。'
        render :edit
      end    
    
    else
        redirect_to root_url
    end
  end

current_userというのは別途用意したメソッドです。ログインユーザーが編集しようとするユーザーと一致しているかチェックします。これが無いと他人が勝手に画像を変える事ができてしまいますからね。

これによってuserテーブルのimageカラムに画像URLが格納されます。

■画像はどこに格納されるのか

で、画像がどこにあるかというとAWSのCloud9では、
/public/uploads/user/image/3/
みたいな所に格納されました。

userという部分がモデル名で、
imageはカラム名
6はuser.idに該当するようにフォルダが自動生成されていました。

ここまでで画像のアップロードは出来ましたが、アップロードしたままのサイズ、ファイル名で入っていきます。

それだと都合が悪いので以下で紹介するrmagickを使います。

画像の加工を行うrmagickをインストールして使う

■まずはimageMagickのインストール。
imageMagickをインストールしてからでないと、rmagicというGemのインストールが出来ないそうです。

幾つかのサイトを拝見しましたがインストールする方法がわからず、テックアカデミーの先生に聞きました。

AWS Cloud9では以下の文でimageMagickのインストールが出来ました。

$ sudo yum -y install ImageMagick ImageMagick-devel

■Gemfileに追記

#画像の加工とか
gem 'rmagick'

■バンドルインストール

$ bundle install
Bundle complete! 21 Gemfile dependencies, 77 gems now installed.
Use bundle info [gemname] to see where a bundled gem is installed.

■image_uploader.rbファイルの編集

以下、こちらの2サイト様を参考に自分仕様に合わせて編集しました。
http://www.workabroad.jp/tech/1118
https://qiita.com/tackey/items/ba68ca8489500b7cb739

編集ファイルはプロジェクトのルート/app/uploaders/配下にあります。

image_uploader.rb


class ImageUploader < CarrierWave::Uploader::Base  

 # リサイズしたり画像形式を変更するのに必要
  include CarrierWave::RMagick

 # 画像の上限を640x480にする
  process :resize_to_limit => [640, 480]

  # 保存形式をJPGにする
  process :convert => 'jpg'

  # サムネイルを生成する設定
  version :thumb do
    process :resize_to_limit => [300, 300]
  end
  
  version :thumb100 do
    process :resize_to_limit => [100, 100]
  end

  version :thumb30 do
    process :resize_to_limit => [30, 30]
  end

  # jpg,jpeg,gif,pngしか受け付けない
  def extension_white_list
    %w(jpg jpeg gif png)
  end

 # 拡張子が同じでないとGIFをJPGとかにコンバートできないので、ファイル名を変更
  def filename
    super.chomp(File.extname(super)) + '.jpg' if original_filename.present?
  end

 # ファイル名を日付にするとタイミングのせいでサムネイル名がずれる
 #ファイル名はランダムで一意になる
  def filename
    "#{secure_token}.#{file.extension}" if original_filename.present?
  end

  protected
  def secure_token
    var = :"@#{mounted_as}_secure_token"
    model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.uuid)
  end


end

サムネイルは100pxパターンと、30pxパターンと2つ作っています。

画像ファイルを読み出す時は、
@user.image.url
とすればオリジナル画像のURLを取り出せます。

サムネイルは
@user.image.thumb30.url
のようにしてURLを取り出せるようになりました。