仕事でCarrierWaveを使っていて、「これってCarrierWave側で簡単な方法用意されてなかったっけ?」と思うことが度々あるので、基本に立ち戻って使い方をおさらいしようと思います。
筆者の環境
- Ruby 2.6.5
- Rails 6.0.2.1
- carrierwave 2.0.2
実装手順
ブラウザ上でファイルを選択し、CarrierWaveを使って開発環境のファイルストレージに保存するところまでを実装します。
例として、ユーザーのプロフィール画像をアップロードできるようにします。userモデルには画像用のカラムとしてimage(string)を用意します。
1. gemのインストール
https://github.com/carrierwaveuploader/carrierwave
# Gemfileに追加
gem 'carrierwave'
2. Uploaderを作ってマウントする
モデルのカラムにCarrierWaveのUploaderクラスを割り当てることで、そのカラムで画像をいい感じに扱えるようになります。
まずはrails generate コマンドを使ってUploaderを作ります。
私はimageカラムに画像を保存したいので、Imageを引数に渡します。
$bundle exec rails generate uploader Image
app/uploaders/image_uploader.rb が生成されるので
model側でimageカラムでこのuploaderを使う設定をします。
# app/models/user.rb
class User < ApplicationRecord
mount_uploader :image, ImageUploader
end
すると、imageカラムがImageUploaderクラスのインスタンスを返すようになります。コンソールで見るとよくわかります。
[1] pry(main)> User.new.image
=> #<ImageUploader:0x000055a12fc82a00
@cache_id=nil,
@file=nil,
@filename=nil,
@identifier=nil,
@model=#<User:0x000055a12fc83680 id: nil, name: nil, email: nil, password: nil, image: nil, created_at: nil, updated_at: nil>,
@mounted_as=:image,
@staged=false,
@versions=nil>
3. Viewを編集する
#new, #edit
新規登録/編集フォームでは、file_field を使って画像アップロード用のフォームを用意します。
<%= form_with(model: user, local: true) do |form| %>
<!-- 他のカラムは省略 --> <div class="field">
<%= form.label :image %>
<%= form.file_field :image %>
<%= form.hidden_field :image_cache # 後述 %>
</div> <div class="actions">
<%= form.submit %>
</div>
<% end %>
ブラウザで確認するとこうなります。
file_field の他に、 hidden_field で image_cacheというのを持たせています。
これは、 file_field だけだとフォームを送信後、バリデーションエラーになって入力画面に戻ってきたときに、ファイルが未選択の状態に戻ってしまい値を保持できないからです。
file_field で生成されるinput属性(<input type=”file”>)は、ユーザーの意図しないファイルが添付されたり、ユーザーのPC内のファイルを抜き取られたりしないように、セキュリティ対策としてvalue属性を設定できない仕様になっています。
CarrierWaveのUploaderは、画像ファイルが送られてくるとその画像の情報をimage_cacheに一時保存します。このimage_cacheの値をhidden_formで受け渡すことによって、file_fieldの値が空になっても画像が保持されるようになります。
#show
次のように書くことで、登録した画像を画面に表示できます。
<%= image_tag @user.image_url if @user.image? %>
まとめ
ここでは最低限の手順になりましたが、実際のサービスでCarrierWaveを使う場合、拡張子やサイズの制限、選択した画像のプレビュー表示、クラウドストレージの使用をすることが多いと思うので、その辺りも試して記事にできたらと思います。