この記事について
ユーザーのアバター画像を設定するためにCarrierwaveを使って画像のアップロード機能を実装していたところ、なぜか画像がアップロードされないエラーが発生した。Railsにおけるmultipart指定について理解が曖昧だったのが原因だったこともあり、その時の対応策を備忘録として残しておきます。
環境
Rails6 (6.1.3)
Carrierwave(2.2.1)
エラー内容
画像ファイルを選択した上でsubmitしても画像が登録されなかった。
パラメータを確認したところ以下のようにavatarのパラメータ値がファイル名になっている。
Parameters: {"authenticity_token"=>"[FILTERED]",
"user_profile"=>{
"name"=>”~~~~~~”,
"avatar"=> “avatar-img.jpg"
}, "commit"=>"更新", "id"=>"1"}
本来は以下のようにActionDispatch::Http::UploadedFile
によってファイル名や画像の形式などが 渡されなければならないはず。
Parameters: {"authenticity_token"=>"[FILTERED]",
"user_profile"=>{
"name"=>”~~~~~~”,
"avatar"=>#<ActionDispatch::Http::UploadedFile:0x00007f66d8e0cae0
@tempfile=# <Tempfile:/tmp/RackMultipart20210420-9-18sadv85o.jpg>,
@original_filename=“avatar-img.jpg",
@content_type="image/jpeg",
@headers="Content-Disposition: form-data; name=\"user_profile[avatar]\"; filename=\"avatar-img.jpg\"\r\nContent-Type: image/jpeg\r\n">
},
"commit"=>"更新", "id"=>"1"}
設定ファイル関連で何かエラーがあるのかと,carrierwave.rb
やuploader.rb
などを確認したが問題はなさそう。
ファイル名がそのまま渡されていたのをヒントにmultipartの指定が怪しいと思い:multipart => true
をformタグに追加してみると無事にファイルアップロードできた!
= form_with model: @user_profile, url: user_profile_path(@user_profile), html: { method: :patch, multipart: true } do |f|
multipartとは
multipart
は複数の種類のデータを一度に扱える形式で、trueを指定することでフォームで文字列だけでなくcsvファイルや画像データなどを扱うことができる。今回はこの指定がされていなかったのでファイルを読み込んでもファイル名が文字列と認識されてしまい、
画像ファイルをアップロードする事が出来ていなかった。
:multipart => trueは必ず指定しないといけない?
しかし、Railsで過去にファイルのアップロードを含むフォームを実装した際には:multipart => true
を指定しなくてもうまく機能していた。
何故なのか不思議だったが、その答えはRailsガイドに記載されていた。
Action View Form Helpers 6 Uploading Files
どうやら form_with
メソッドでモデルを指定したフォーム内にfile_field
使用すると、自動的に:multipart => true
が適用されるらしい。
<%= form_with model: @person do |f| %>
<%= f.file_field :picture %>
<% end %>
モデルを指定しない場合は:multipart => true
を指定しないといけない
<%= form_with url: "/uploads", multipart: true do |f| %>
<%= f.file_field :picture %>
<% end %>
今回はform_with
メソッドでモデルを指定したフォームを使用していたが、アップロード画像のプレビュー表示をReactを使って実装していたため、file_fieldを使用していなかった。
.field
= f.label :avatar
= react_component("UserAvatarPreview")
そのため、ファイルアップロードのためには:multipart => true
を指定しないといけなかったらしい。
まとめ
-
form_with
メソッドでモデルを指定したフォーム内にfile_field
使用すると、自動的に:multipart => true
が適用される - モデルを指定していなかったり
file_field
使用していない場合は:multipart => true
を指定しなければならない