Railsでファイルをアップロードしてpublicディレクトリに保存したあとに表示する
WebアプリなんてテキストとDBのIO処理だけで、画像やバイナリなんていらないぜ!!
…そう考えていた時期がたくみにもありました。
というわけで、Railsで画像をアップロードして、その画像を表示する方法を解説します。
Railsプロジェクトの準備
まずはRailsのプロジェクトを作成します。
今回は uploader という名前で rails new を実行します。
| shell | |
|---|---|
| 1 | rails new uploader |
| 2 | cd uploader |
| 3 | rails g controller fileuploads index create new |
今回はDBを使わずに画像を扱います。
そのため、controllerを指定してscaffoldを実行します。
index,create,newのアクションのそれぞれの役割は以下のとおりです。
- index ... アップロードした画像の一覧表示。
- new ... 画像をアップロードするためのフォームを表示。
- create ... 画像を保存する処理を実行。処理完了後はindexかnewに遷移するため、ビューを持たない。
ルーティングの変更
rails g controller コマンドで作成したコントローラとアクションは自動でroutes.rbにルーティングを追記してくれますが、
すべて GET アクセスが前提になります。
なので、resourcesを使用したルーティングに書き換えます。
| config/routes.rb | |
|---|---|
| 1 | Rails.application.routes.draw do |
| 2 | resources :fileuploads, only: [index, :create, :new] |
| 3 | end |
rake routesコマンドを実行するとルーティングが下記のようになります。
| routing information | |
|---|---|
| 1 | Prefix Verb URI Pattern Controller#Action |
| 2 | fileuploads GET /fileuploads(.:format) fileuploads#index |
| 3 | POST /fileuploads(.:format) fileuploads#create |
| 4 | new_fileupload GET /fileuploads/new(.:format) fileuploads#new |
multipartを有効にしてformを作成
今回はDBを使用しないため、form_forではなく、form_tagを使用します。
画像を送信する場合は、multipartを有効にする必要があります。
もし、multipartを無効のまま送信すると、Railsにはファイル名しか受信できないので注意してください。
| app/views/new.html.erb | |
|---|---|
| 1 | <%= form_tag '/fileuploads', method: 'post', multipart: true do %> |
| 2 | <label>ファイル</label> |
| 3 | <%= file_field :fileupload, :file %> |
| 4 | |
| 5 | <%= submit_tag 'Send file' %> |
| 6 | <% end %> |
引数に multipart: true と指定するとmultipartが有効になります。
アップロードされたファイルをpublicディレクトリへ保存
アップロードされた画像はブラウザから読み取れる場所に保存しておかないと
ユーザに閲覧させることができません。
方法としては、DBのBLOBに保存して表示する方法やpublicフォルダに保存する方法、その合わせ技など
たくさんありますが、今回はシンプルに実装できるpublicフォルダに保存する方法を実装します。
createメソッドとfileupload_paramメソッドを実装していきます。
| app/controllers/fileuploads_controller.rb | |
|---|---|
| 1 | def create |
| 2 | |
| 3 | uploaded_file = fileupload_param[:file] |
| 4 | output_path = Rails.root.join('public', uploaded_file.original_filename) |
| 5 | |
| 6 | File.open(output_path, 'w+b') do |fp| |
| 7 | fp.write uploaded_file.read |
| 8 | end |
| 9 | |
| 10 | redirect_to action: 'index' |
| 11 | |
| 12 | end |
| 13 | |
| 14 | private |
| 15 | def fileupload_param |
| 16 | params.require(:fileupload).permit(:file) |
| 17 | end |
fileupload_paramメソッドはstrongパラメータです。
params[:file]でも代用可能ですが、こちらを使うほうがよりセキュアにできます。
アップロードしたファイルはRailsが自動的に ActionDispatch::Http::UploadedFile クラスのインスタンスに変換してくれます。
このActionDispatch::Http::UploadedFileクラスにはoriginal_filenameメソッドとreadメソッドがあります。
original_filenameメソッドはアップロードしたファイルの名前を取得することができます。
readメソッドはファイルの中身を取得することができます。
publicフォルダにアップロードファイルを保存するため、 Rails.root でRailsのルートディレクトリのパスを取得し、
そこに、 public と ファイル名 を結合して出力先のパスを作ります。
また、画像をアップロードするため、バイナリを書き込めるモード('w+b')を指定し、
保存場所は先ほど生成した出力先のパスを指定してファイルをオープンします。
fp.writeメソッドで実際にファイルに書きこんでpublicディレクトリに画像ファイルの配置します。
最後にindexアクションのビューをレンダーして完了します。
アップロードされた画像を表示する
publicフォルダに画像ファイルがアップロードされてゆくので、
このアップロードされたファイルを表示する処理を作成していきます。
まずは、コントローラのindexアクションを作ります。
| app/controllers/fileuploads_controller.rb | |
|---|---|
| 1 | def index |
| 2 | |
| 3 | @img_paths = [] |
| 4 | jpgs = Dir.glob(Rails.root.join('public', '*.jpg')) |
| 5 | jpgs.each do |png| |
| 6 | @img_paths.push('/'+File.basename(png)) |
| 7 | end |
| 8 | |
| 9 | end |
@img_pathsインスタンス変数に画像パスをセットしていきます。
Dir.globメソッドを使って、publicディレクトリ以下にあるjpgファイルの一覧を取得します。
このままだと、システムのファイルパスになるので、File.basenameで画像名だけ取得し、
URLでアクセスできるように、先頭に/(スラッシュ)を追加して@img_paths配列に追加してきます。
(publicフォルダに設置しているファイルはDocumentRootになるため、/filenameでアクセスできるようになる)
次にindex.html.erbを編集します。
| app/views/index.html.erb | |
|---|---|
| 1 | <h1><%= link_to 'Append jpg', new_fileupload_url %></h1> |
| 2 | |
| 3 | <% @img_paths.each do |img_path| %> |
| 4 | <img src="https://cre8cre8.com/<%= img_path %>"> |
| 5 | <% end %> |
link_toで作っているリンクは画像をアップロードする画面へのリンクです。
@img_pathsインスタンス変数に格納したパスをeachで回して、
imgタグのsrc属性にセットしていきます。
これで、indexアクションにアクセスしたときにアップロードした画像が表示されます。
まとめ
Railsで画像をアップロードするのはおもったより簡単だったのではないでしょうか?
それでは、アップロードするためのまとめにいきたいと思います。
- ファイルをアップロードするときはformのmultipartを有効にする
- ファイルをアップロードしたparams変数はActionDispatch::Http::UploadedFileクラスのインスタンスに変換される
- original_filenameメソッドでアップロードしたファイルの名前を取得できる
- readメソッドでファイルの中身を取得できる
上記の内容を押さえておけば、DBに保存するのも少し応用するだけでできるようになります。
ファイルアップロードはアップロード処理よりも、データの加工やトリミング、アクセス権限などで
大きく苦労すると個人的に考えています。
また機会があればこのあたりの苦労話を実例を交えて話したいと思いますw
それでは、Railsでファイルのアップロード処理をエンジョイしてください!でわでわ♪