Rails – ファイルのアップロード~ゼロから組み立て

概要

  • Railsで画像ファイルをアップロードする処理を整理する
  • ユーザーのプロフィール画面のアップロードと更新を例にする
  • 更新時、前の画像は削除する
  • 画像ファイル名の衝突をさけるため、ランダム文字列を生成してファイル名にする

フォームからのアップロード情報を受け取る

ビュー

以下のビューが表示されるとファイル選択要素が表示される。

file_fieldに対するキーは:upload_fileと設定しているが、これはモデルに存在しないキーでもよい。

ここでは登録済みユーザーのプロフィール画像を編集する想定で、PATCHメソッドでupdateコントローラーに送信される。

コントローラー

フォームから送られたアップロードファイルに関するデータはparamsに格納されていて、以下で取得できる。

params[:モデル][:file_fieldで設定したキー]

ここではアップロードファイルが指定されているときにupload_fileにアップロードファイルの内容を保存し、その中のoriginal_filenameの値をupload_file_nameに保存している。

UploadedFileクラス

file_fieldからparamsに保存されるのはUploadedFileクラスのインスタンス。

たとえば上のupload_fileの内容は以下の様になっていて、original_filenamecontent_typeがインスタンス変数として参照できる。

また、画像ファイルの実体はreadメソッドで読みだすことができて、これをファイルの保存時に使う(readメソッドは1回しか呼び出せないらしい)。

permitの設定は不要

フォームから得られるUploadedFileオブジェクトをparamsで取得しているが、これに対してrequire.permitに含める必要はない。

Railsのモデルの仕組みによって自動的に入力・保存するものではないため。

ファイルの書き込み

基本手順

  1. アプリケーションの中のファイル保存ディレクトリーを取得
  2. 上記とファイル名を組み合わせたフルパスを取得
  3. 画像ファイルの書き込み

コントローラー

コントローラーのupdateアクションのみ示す。

流れは以下のとおり。

まず事前にプロジェクトディレクトリー下のpublicに必要に応じてサブディレクトリーをつくっておく

/home/.../ex_bbs/public/user_images

Rails.rootはプロジェクトディレクトリーのフルパスを保持したPathオブジェクト

Rails.root.join()でパスにディレクトリーをつなげていく

upload_dir = Rails.root.join("public", "user_images")

upload_dirにファイル名をつなげてファイルのフルパスにする

upload_file_path = upload_dir + upload_file_name

ファイルのフルパスとファイルの実体を指定して、画像ファイルを書き込む。

File.binwrite(upload_file_path, upload_file.read)

データベースへの登録

基本手順

  1. フォームから得られたparamsにヘルパーでパーミッションを適用
  2. その結果にアップロードファイルのファイル名をマージして登録用のパラメーターとする
  3. 登録用パラメーターでデータベースに登録

コントローラー

パラメーターヘルパー

コントローラーで多用されるヘルパーは以下のような形。

フォームから得られたパラメーター(user_paramsの戻り値)は以下のような内容。画像選択以外の要素がないのでパラメーターは空になっている。

パラメーターの調整

フォームで得られた:upload_fileからデータベースに登録するファイル名を取り出し、パラメーターにマージする。

new_user_params = user_params.merge({image_file_name: upload_file_name})

マージ後のnew_user_paramsの内容は以下のとおりで、空だった内容に:image_file_nameが追加されている。

データベースの更新

ユーザーモデルのupdateに調整したパラメーターを与えてデータベースを更新する。

@user.update(new_user_params)

ファイルの削除

データ更新の際に、現在登録されているファイルを削除する場合。

ファイル名の重複防止~ランダム文字列

アップロードファイル名にoriginal_filenameを使うと、ユーザー側で指定したファイル名が衝突する可能性がある。その場合、あとから登録した画像で上書きされてしまう。

そこで、ファイル名にランダム文字列を使って、実質上衝突が起こらないようにする。

同じ機能は投稿記事のコントローラーでも利用し、アプリケーションに共通なので、application_helper.rbに記述する。

app/helpers/application_helper.rb

app/controllers/application_controller.rb

そして、アップロード画像のファイル名をランダム文字列とする。このファイル名はデータベースの更新と画像ファイルの保存に共通して使われる。

こうすることで、複数のユーザーが同じファイル名で画像をアップロードしても、ユーザーごとに異なる名前となるため、ファイル名の衝突がおきない。

まとめ

画像ファイルのアップロードのためのファイル群をまとめておく。ユーザーのプロフィール画像をアップロードするケースを例にする。

editアクション

  • ユーザー操作などでルーティングされ、画像アップロードを含むページを表示する
  • ビューで使うセッション中のユーザーのインスタンスをインスタンス変数に保存する

app/controllers/users_controller.rb

ビュー

  • 画像アップロードを含むページ
  • ボタンを押すとPATCHメソッドでupdateアクションにデータが送られるようルーティングしている

app/views/users/edit.html.erb

updateアクション

フォームからのデータを受け取り、以下の処理を行う。

  • 画像の保存(古い画像の削除)
  • データベースのファイル名の更新

app/controllers/users_controller.rb

ランダム文字列ヘルパー

ファイル名の衝突を防ぐランダム文字列生成のヘルパー。

app/helpers/application_helper.rb

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です