初めてのRailsアプリ(その3)
railsubject_03
初めてのRailsアプリ(その3)
railsのモデルって本当にモデル?ただのORマッパじゃね?
はじめに
今回はファイルをアップロードを題材に、共通モジュールとトランザクションに関して勉強する
4. ファイルアップロード
ファイルを適当なところにアップします。ポイントは、以下の2点。
- ファイルを保存する機能をくくりだし、共有モジュール化する
- ファイル自体はファイルシステムとして保存し、ファイルの情報はDBに保存する
ER図
今回はここ
変更点
前回より以下の点を変更する。
モデル
変更なし
ビュー
viewヘルパをfile_fieldに変更する
モジュール
概要
ファイルを保存する共有モジュールを作成する。提供するメソッドは以下
- check_file
- ファイルの拡張子とサイズをチェックする
- チェック条件はデフォルトを用意しているが、変更も可能
- NGの場合は例外を発生させる
- upload_file
- ファイルを保存する
- 保存先のディレクトリを指定できる
- 指定したディレクトリがない場合は作成する
実装
#ファイルアップロードの部品を提供する共通モジュール
module UploadModule
# 定数
PATH = "/Users/7010oncajon/Documents/workspace/railsubject/public/files/"
PERMS = [".md", "txt"]
LIMIT = 200.megabyte
# エラーメッセージ
PERMS_ERROR = "ファイルの種別が想定外です。"
LIMIT_ERROR = "ファイルのサイズが想定外です。"
IO_ERROR = "ファイルの書き込みに失敗しました。"
# 入力されたファイルのチェックを行う
def check_file(file, perms=PERMS, limit=LIMIT)
# ファイル情報の取得
file_name = file.original_filename
file_size = file.size
# ファイルのチェック
if !perms.include?(File.extname(file_name).downcase)
raise PERMS_ERROR
elsif file_size > limit
raise LIMIT_ERROR
end
end
# ファイルを保存する
def upload_file(file, dir)
begin
#
dir = PATH + dir
path = dir + file.original_filename
# サブディレクトリの作成
FileUtils.mkdir_p(dir)
# ファイルの書込み
File.open(path, "wb"){|f| f.write(file.read)}
rescue => e
Rails.logger.error(e.message)
raise IO_ERROR
end
end
module_function :upload_file
module_function :check_file
end
配置
このモジュールは"(アプリケーションルート)/lib/upload_module.rb"に保管し、このディレクトリをautoloadで切るようにしておく。
コントローラ
ファイルをrequireして、モジュールをincludeする。いわゆるひとつのMix-in。
upload_controllerの新規作成時(create)の処置を以下に示す。(結構大幅に変わったので、差分ではなく全文を表示)
def create
@upload = @subject.uploads.build(params[:upload])
# ファイル取得
file = @upload.path
# トランザクション処理(ファイルの保存とDB更新を同一のトランザクションで行う)
Upload.transaction do
# ファイルのチェック
check_file(file)
# idを取得するために一旦モデルを保存(もっといい方法ありませんか?)
@upload.save!
# ファイルの保存先pathを作成し、モデルを保存
dir = @subject.id.to_s + "/" + @upload.id.to_s + "/"
@upload.path = dir + file.original_filename
@upload.save!
# ファイルを保存
upload_file(file, dir)
end
# 正常終了の処理
redirect_to [@subject, @upload], :notice => 'Upload was successfully created.'
rescue => e
# 例外発生時の処理
redirect_to new_subject_upload_url(@subject, @upload), :notice => e.message
end
完成形
なお、ファイルチェックやファイル保存時に例外が発生した場合は、DBへのinsertもロールバックされているはずです。
終わりに
次にやりたいことは以下
- groupでsubjectを選択する際のUI高度化
- mailでメールを送る
- has_many thoughアソシエーションをさんすくみでやる