こんにちは!鳥貴士です!
今回はRails5.2から追加されたActiveStorage機能を使ってファイルのアップロード、そしてダウンロードリンクの実装をします!
具体的にはpdfファイルのアップロード、ダウンロードを行います。
イメージとしてはユーザーがたくさんのpdfモデルを持っている、そのpdfモデルには1つのpdfファイルが割り当てられる感じです。
pdf以外でもできますが、今回作ったものがpdf絡みのものだったのでこんな例になってます。
この例でのファイルの保存先はlocalですが、AWSやGCPなどを使うことも可能です。
セットアップ
まず最初にRailsにActiveStorageのためのテーブルを作成します。
$ rails active_storage:install
$ bundle exec rake db:migrate
を実行し、activestorage_blobとactivestorage_attachmentテーブルが作成されていることを確認します。
マイグレーションに関しては飛ばします。
マイグレーションは見なくてもActiveStorageはわかります。
モデルの設定
テーブルが作成出来たら個々のモデルの設定に移りましょう。
ユーザー
まずユーザーはたくさんのpdfを持っている仕様でしたね。
ですからユーザーモデルは以下のような関係性を保持しています。
これはActiveStorageとは関係のない普通のコードです。
ログインとかパスワードとかは関係ないので省略。
class User < ApplicationRecord has_many :pdfs end
pdfモデルには一つのpdfファイルが紐づけられています。
そしてpdfの所有者すなわちアップロード者はただ一人です。
ですからpdfモデルは以下のような関係性を保持しています。
ただなぜoptional: trueを指定したのかは忘れてしまった。
確かアップロード周りでこけていたはず。
class Pdf < ApplicationRecord belongs_to :user, optional: true has_one_attached :pdf_file end
ルーティングの設定(抜粋)
見ればわかる
get "upload", to: "pdf_upload#new" post "upload", to: "pdf_upload#create" resources :uploaded_pdfs, only: [:index] do end
コントローラの設定
諸々のページについては省略して、アップロードとダウンロードに関係するコントローラのみ取り上げます。
まずはアップロードから。
class PdfUploadController < ApplicationController def new @pdf = Pdf.new end def create @pdf = Pdf.new(upload_params) if @pdf.save redirect_to uploaded_pdfs_path end end private def upload_params params.require(:pdf).permit(:user_id, :name, :details, :pdf_file) end end
という感じ。
newメソッドは説明いらないでしょ。
createメソッドでupload_paramsを通ってきたデータをデータベースに保存し、完了したら自分がアップロードしたものをみられるページに飛びます。
アップロードしたものがみられるページでは、アップロードしたファイルのダウンロードが可能です。
ダウンロード側のコントローラはこのようになっています。
class UploadedPdfsController < ApplicationController def index @pdfs = @current_user.pdfs end end
です。説明ほぼいらないですよね。
Userモデルに定義されたhas_many :pdfsの関係ということぐらいです。
ビューの設定
View周りは結構苦手なんですよね。
form_withのオプションわけわからん、書き方何通りあるの。
あとerbなのでタグが<%…%>とか<%=…%>なのも慣れない。
かといってangularみたいになるのもインポートしたりまわりが面倒だし…
まぁ使っているうちに慣れるのはわかってますけどね。
Haml使えっていうのもわかります、言われたことないけど。
言ってることはわかり哲也ですがやる気がでません。
さぁ文句はここまでにしてコードを見ましょう。
アップロードページ
余計なコードは消したのですっきりしています。
<%= form_with model: @pdf, url: upload_path do |f| %> <div> <%= f.label "name" %> <%= f.text_field :name %> </div> <div> <%= f.label "details" %> <%= f.text_area :details %> </div> <div> <%= f.label "select" %> <%= f.file_field :pdf %> </div> <%= f.hidden_field :user_id, value: @current_user.id %> <%= f.submit "send" %> <% end %>
form_withでフォームを生成しています。
file_fieldでファイルを指定、hidden_fieldでuser_idをくっつけてsubmitします。
form_withに何も指定しないとpostとして送信されるので、フォームで生成されたデータはPdfUploadController#createに投げられます。
ダウンロードページ
@pdfsはログイン中のユーザーがアップロードしたpdfの配列を返します。
そのためイテレータで処理します。
<% @pdfs.each do |pdf|| %> <%= link_to pdf.name, rails_blob_path(pdf.pdf_file) %> <%= pdf.details %> <% end %>
という具合です。
リンクを生成するためにlink_toを使い、どこからファイルを持ってくるのかはrails_blob_pathメソッドに対してattachしているファイルを指定してあげればよいです。
これでファイルのアップロード、そしてダウンロードリンクの生成ができるようになりましたね!
それではまた!