【Rails】carrierwaveを使う
今回はtaskleafに取り付ける想定。
CarrierWaveというgemがファイルアップロード操作の有名なもの。
入れる
code:gemfile
gem 'carrierwave', '~> 2.0'
bundle install
入らん。
code:txt
Fetching mini_magick 4.12.0
ArgumentError: wrong number of arguments (given 4, expected 1)
An error occurred while installing mini_magick (4.12.0), and Bundler cannot continue.
Make sure that gem install mini_magick -v '4.12.0' --source 'https://rubygems.org/' succeeds before bundling.
In Gemfile:
carrierwave was resolved to 2.2.6, which depends on
image_processing was resolved to 1.12.2, which depends on
mini_magick
mini_magickを先に入れてくれよなということ
mini_magickを入れる
入らん。
これを使うために、OS自体にimagemagickが必要らしい。
brew install imagemagickで入れる。
入れた後もはいんね〜
キャッシュとかの可能性
Bundler のキャッシュをクリアする: Bundler のキャッシュをクリアすることで、Gemfile.lock や依存関係のキャッシュを消去することができます。ターミナルで次のコマンドを実行します。
bundle clean --force
これにより、Bundler のキャッシュがクリアされ、新しい状態に戻ります。
gem キャッシュをクリアする: インストールされた gem のキャッシュをクリアすることもできます。ターミナルで次のコマンドを実行します。
gem clean
これにより、gem のキャッシュがクリアされます。
上2つコマンド打ったら入った。
code:gemfile
gem 'carrierwave'
gem 'mini_magick'
気を取り直してcarrierwaveを使う
アップローダーを作る
rails generate uploader Icon
後ろで指定した文字がファイル名称になる。これならapp/uploaders/icon_uploader.rbとなる
...
コマンド打って止まった。rails cで止まる挙動に近い。
code:terminal
ps aux | grep -i spring
skoni 10625 0.0 0.0 33591680 88 s012 R+ 5:51PM 0:00.00 grep -i spring
skoni 87037 0.0 0.1 34811700 39644 ?? Ss 5:20PM 0:01.68 ruby -I /Users/skoni/.rbenv/versions/2.5.1/lib/ruby/site_ruby/2.5.0 -I /Users/skoni/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/spring-2.1.1/lib -e require 'spring/application/boot'
skoni 96243 0.0 0.0 34476188 14436 s016 S 3:28PM 0:00.41 spring server | taskleaf | started 2 hours ago
消す
kill -9 87037とkill -9 96243で。
動いた。
code:terminal
rails generate uploader Icon
/Users/skoni/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/digest-3.1.1/lib/digest.rb:20: warning: already initialized constant Digest::REQUIRE_MUTEX
/Users/skoni/.rbenv/versions/2.5.1/lib/ruby/2.5.0/digest.rb:6: warning: previous definition of REQUIRE_MUTEX was here
Running via Spring preloader in process 11226
create app/uploaders/icon_uploader.rb // 動いた
アップローダーを調整する
コメントアウトされた箇所を取ったりなど。バリデーションやデフォルトの値を設定できる。
アップローダーをモデルのカラムに取り付ける
今回はusers.iconに紐付ける。iconカラムが無いなら作る。
modelで指定
code:User.rb
# アップローダの取り付け
mount_uploader :icon, IconUploader
アップロードする準備を整える
コントローラのストロングパラメータに追加する
ファイルのアップロード処理を書く必要は無い。
ビュー側にf.labelとf.file_fieldを用意する
エラーを解消する
上記の設定で見ると以下のエラーに遭遇した。
code:txt
NameError (uninitialized constant User::IconUploader):
code:config/environment.rb
# carrierwave アップローダーとmodel取り付け時の以下のエラーを防ぐ
# NameError (uninitialized constant User::IconUploader): など
require 'carrierwave/orm/activerecord'
いけた。https://scrapbox.io/files/6609293a33c04200251ae143.png
画像がある場合だけ出すようにする
結論
code:slim
tr
th= User.human_attribute_name(:icon)
td= image_tag @user.icon.url if @user.icon.url.present?
やったこと
アップローダー調整
デフォルトをコメントアウトしておく。
code:app/uploaders/icon_uploader.rb
def default_url
# 'sample.jpg'
end
なぜか
code:rb
# 画像をアップしてないユーザーにやった場合
@user.icon.url
=> "/uploads/user/icon/5/default.png"
値が返ってきてしまい、nil判定できないから。
コメントアウトしておけばnilとなる。
code:rb
@user.icon.url
=> nil
slim調整
元々td= image_tag @user.icon.url if @user.icon.nilを否定の形にする想定で書いていた
nilじゃ無い時に<img>を出したいから
だったら、値があったらを意味するpresent?で良い。
データをs3に格納する場合など
code:rb
qrcode = RQRCode::QRCode.new(url)
@qr = qrcode.as_png(...)
# まずRailsアプリケーション内のupload直下に格納する場合
folder_path = "#{Rails.public_path}/uploads/qrcode"
FileUtils.mkdir_p(folder_path) unless File.directory?(folder_path) # pathがなければディレクトリ作成
IO.binwrite("#{folder_path}/#{encripted_file_name}",@qr)
まずはアップローダクラスをnewで呼べるようにする。
引数には、関連するオブジェクトを指定しておく。Taskというオブジェクトで作った場合なら、
アップローダの用意
$ rails generate uploader Qrcode
code:qrcode_uploader.rb
class QrcodeUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
def store_dir
...
carrierwave側の格納先をs3とすることで操作できる。
今回はアップローダ全てを管理するファイルを操作して設定する。
config/initializers/carrierwave.rbが一般的にそのファイル
前提
awsアカウントがある
awsバケットがある
awsアカウントがそのバケットを操作できる権限を持つ
code:carrierwave.rb
# バージョンによっておそらく書き方は異なるが、おそらくこんな感じ
CarrierWave.configure do |config|
credentials = {
provider: 'AWS',
aws_access_key_id: 'xxx',
aws_secret_access_key_id: 'ooo',
region: 'ap-northeast-1'
# fogで設定して
config.storage = :fog
config.fog_credentials = credentials # 上の配列を渡す
config.fog_directory = 's3_bucket_name'
end
:fogを使えばaws認証に設定ができる。なおgemなので使う場合は落としておく必要あり。
ちなみにconfig:storage = :fileとすればrailsアプリのpublicフォルダに入る。
carrierwave.rbの設定が済んだので、Uploaderは全てこのファイルの設定に従って格納先を指定するようになる。
コントローラ側で呼ぶ
code:controller.rb
uploader = QrcodeUploader.new(@task)
この場合引数には、QrcodeUploaderで参照するオブジェクトを渡す。
code:qrcode_uploader.rb
# @taskを引数としたなら、modelはいわゆる @task のことになる
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
model.class.to_s_unoderscore
モデルのクラス名をスネークケースにして渡す。
PartyMemberならparty_memberになる
mounted_as
carrierwaveでアップロードされるファイルとカラムをマウントしていた場合、そのカラムを呼ぶ。
code:task.rb
# モデルでアップローダのマウント定義をしておくと、
# アップローダ側の#{mounted_as}で:qrcode_pathを呼ぶことができる
mount_uploader :qrcode, QrcodeUploader
なお今回はformから画像をアップするわけではなく、コントローラ側で作った画像を格納するのでマウントはしない。
マウントするとそのカラムについての情報がアップローダ側で管理されることになる
@task.update(qrcode: 'image_name.png')とした場合でも、反映されないケースに遭遇したりする。(した)
アップローダの設定を行ったならコントローラ側で最終的には下記のように書くとよい
code:controller.rb
encripted_file_name = 'image.png'
qrcode = RQRCode::QRCode.new(url)
@qr = qrcode.as_png(...)
# tmp的な役割で、作ったQRコード画像はRailsアプリケーション内のupload直下に格納しておく
folder_path = "#{Rails.public_path}/uploads/qrcode"
FileUtils.mkdir_p(folder_path) unless File.directory?(folder_path) # pathがなければディレクトリ作成
IO.binwrite("#{folder_path}/#{encripted_file_name}",@qr)
# carrierwaveにて作成した、QrcodeUploaderをnewで呼ぶ。
uploader = QrcodeUploader.new(@task)
# uploader経由でstoreする
# railsアプリのpublic/upload/qrcode/image.pngを指定する
uploader.store!(File.open("#{folder_path}/#{encripted_file_name}"))
# s3に保存したので元データのpublic/upload/qrcode/image.pngを消す
File.delete("#{folder_path}/#{encripted_file_name}") if File.exist?("#{folder_path}/#{encripted_file_name}")
# task.qrcodeカラムにファイル名を格納しておく
@task.update(qrcode: encripted_file_name)
これでOK