December 04, 2013

rails でファイルをアップロードする際の挙動

プログラミング
RubyOnRails

経緯

前回,画像ファイルをアップロードして DB に保存する記事を書きました.

実際にファイルはどうやって DB に保存されるのかについて補足します.

params をみる

前回と同じプロジェクトで進めます.

def create
  binding.pry # <= ここの params を確認したい
  @user = User.new(user_params)
  @user.icon = params[:user][:icon].read
  @user.icon_content_type = params[:user][:icon].content_type

実際にファイルをアップロードした時のパラメータを見てみます.

[1] pry(#<UsersController>)> params[:user][:icon]
=> #<ActionDispatch::Http::UploadedFile:0x007fbdcbbe0ba0
@content_type="image/jpeg",
@headers=
"Content-Disposition: form-data; name=\"user[icon]\"; filename=\"laughing_man.jpg\"\r\nContent-Type: image/jpeg\r\n",
@original_filename="laughing_man.jpg",
@tempfile=
#<File:/var/folders/j6/4l756wsn2538zn3b31sgf3yc0000gn/T/RackMultipart20131204-93143-5kjzat>

ということで,ブラウザ上のフォームからアップロードしたファイルの正体は ActionDispatch::Http::UploadedFile のようです.

Tempfile

この @tempfile がファイルの実体です.

[2] pry(#<UsersController>)> params[:user][:icon].tempfile
=> #<File:/var/folders/j6/4l756wsn2538zn3b31sgf3yc0000gn/T/RackMultipart20131204-93143-5kjzat>
[3] pry(#<UsersController>)> params[:user][:icon].tempfile.class
=> Tempfile
[4] pry(#<UsersController>)> params[:user][:icon].tempfile.path
=> "/var/folders/j6/4l756wsn2538zn3b31sgf3yc0000gn/T/RackMultipart20131204-93143-5kjzat"

ここからわかるように,アップロードしたファイルは一時的に /var/folders/j6/4l756wsn2538zn3b31sgf3yc0000gn/T/RackMultipart20131204-93143-5kjzat のような場所に格納されます.

※ 通常は保存されず,すぐに削除されます.

ちなみに,

ActionDispatch::Http::UploadedFile に対して read メソッドを呼ぶと,バイナリが取得できますが,一度呼ぶと取得できなくなります.

[5] pry(#<UsersController>)> params[:user][:icon].read
=> "\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x92\x00\x00\x00\x92\b\x06\x00\x00\x00\xAE{\x93\x8E\x00\x00\x00\x19tEXtSoftw
...
[6] pry(#<UsersController>)> params[:user][:icon].read
=> ""

なので,以下のようにプリントデバッグしようとするとはまります(はまりました).

def create
  @user = User.new(user_params)
  pp params[:user][:icon].read # <= ここで読んでしまうと
  @user.icon = params[:user][:icon].read # <= ここでは取得できない
  @user.icon_content_type = params[:user][:icon].content_type
  ...