cre8cre8
AskMe♥

Railsでファイルをアップロードしてpublicディレクトリに保存したあとに表示する

WebアプリなんてテキストとDBのIO処理だけで、画像やバイナリなんていらないぜ!!
…そう考えていた時期がたくみにもありました。
というわけで、Railsで画像をアップロードして、その画像を表示する方法を解説します。

Railsプロジェクトの準備

まずはRailsのプロジェクトを作成します。
今回は uploader という名前で rails new を実行します。

shell
1rails new uploader
2cd uploader
3rails g controller fileuploads index create new

今回はDBを使わずに画像を扱います。
そのため、controllerを指定してscaffoldを実行します。
index,create,newのアクションのそれぞれの役割は以下のとおりです。

ルーティングの変更

rails g controller コマンドで作成したコントローラとアクションは自動でroutes.rbにルーティングを追記してくれますが、
すべて GET アクセスが前提になります。
なので、resourcesを使用したルーティングに書き換えます。

config/routes.rb
1Rails.application.routes.draw do
2  resources :fileuploads, only: [index, :create, :new]
3end

rake routesコマンドを実行するとルーティングが下記のようになります。

routing information
1Prefix Verb URI Pattern                Controller#Action
2fileuploads GET  /fileuploads(.:format)     fileuploads#index
3       POST /fileuploads(.:format)     fileuploads#create
4new_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
1def 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
12end
13
14private
15def fileupload_param
16  params.require(:fileupload).permit(:file)
17end

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
1def 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
9end

@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で画像をアップロードするのはおもったより簡単だったのではないでしょうか?
それでは、アップロードするためのまとめにいきたいと思います。

上記の内容を押さえておけば、DBに保存するのも少し応用するだけでできるようになります。
ファイルアップロードはアップロード処理よりも、データの加工やトリミング、アクセス権限などで
大きく苦労すると個人的に考えています。
また機会があればこのあたりの苦労話を実例を交えて話したいと思いますw

それでは、Railsでファイルのアップロード処理をエンジョイしてください!でわでわ♪

≪ 前の記事
CircleCIとRSpec使って継続的インテグレーションしてみる
次の記事 ≫
たった30行でPython3のマルチスレッドを使ってみる

いいねやコメントを送っていただけると中の人がしっぽ振って大喜びします♪

あなたへのおすすめの記事