Liftでファイルアップロード
Scalaで書かれたWebフレームワークであるLiftを最近結構触っている。日本語の情報はおろか、英語の情報もそれ程豊富な訳じゃないんだけど、色々調べながらってのが結構面白い。
■ファイルアップロードの基本
今回はファイルのアップロード。"Lift ファイルアップロード"で検索するとこのページが出てきた。
Shtml.fileUpload を使うとアップロードされたファイルの内容を取得できるので、それを煮るなり焼くなり。詳しくは先ほどのページを参照。あるいはExploring Liftのこの辺。
■CRUDと組み合わせ
LiftでCRUDをやるにはCRUDifyというtraitを使う。以前CRUDifyに関するエントリを書いたので、そちらを参照。
Modelにバイナリのフィールドを追加してちょっと何かやればファイルアップロードが実現できるか、というと実はそんな事はなくて、いくつかの問題点がある。
1つめは些細な問題だけど、CRUDifyで作られる<form>タグにmultipart属性が付いていないという問題。MLにも同様の話題があり解決策もそこで提示されている(こちら)。_createTemplateと_editTemplateというメソッドをオーバーライドして属性をつけるという、まぁかなり直接的なやり方。
object Foo extends Foo with LongKeyedMetaMapper[Foo] with CRUDify[Long, Foo] { import xml.{ NodeSeq, Text, UnprefixedAttribute, Null } override def _createTemplate = super._createTemplate % new UnprefixedAttribute("multipart", Text("true"), Null) override def _editTemplate = super._editTemplate % new UnprefixedAttribute("multipart", Text("true"), Null) }
これで値が受け取れるようになる。一般的な選択肢としては、ファイル(やその他のStream)に書き込むかDBに書き込むかのどちらかだと思う。
ファイルに書き込む例はこちらに出てる。DBに書き込む例は、まとまってる情報がなかったので次の項で説明。
ファイルアップロードに関する注意事項として、Shtml.fileUploadはファイルアップロードが完了されてからセットされるので、巨大なファイルのアップロードには使えない。巨大なファイルをStreamで受け取りつつファイルに書き込むと言う事は出来ない。
■受け取ったファイルをDBに書き込む
もっと良い方法があるかもしれないけど、とりあえず自分がやった方法。上の方で紹介したMLのやりとりを参考にした。
Modelにバイナリのフィールド、アップロードされたファイルをチェックしてそのバイナリフィールドにセットするメソッドを定義する。具体的には以下のようになる。
class Foo extends LongKeyedMapper[Foo] with IdPK { def getSingleton = Foo object name extends MappedString(this, 20) // 画像を格納するフィールド object image extends MappedBinary(this) { override def dbDisplay_? = false override def writePermission_? = true } // 画像のパスを格納するフィールド object imagePath extends MappedBinary(this.asInstanceOf[MapperType]) { def notEmpty(input: String) = { if (input.isEmpty) { //ファイルが空 } else Nil } override def _toForm = { Full(SHtml.fileUpload(saveFile _)) } override def displayName = "file" } // 受け取ったファイルをチェックして、フィールドの値をセット private def saveFile(fp: FileParamHolder): Unit = { println("*** saveFile ***") fp.file match { case null => { println("saveFile: null") } case x if x.length == 0 => { println("saveFile: empty file") } case _ => { println("saveFile: default") // 画像ファイルのフォーマットを判別する // ImageHandlerのコードは、ここでは省略 code.lib.ImageHandler.getFormatName(fp.file) match { case Some(format) => { this.image(fp.file) this.imageFormat(format) } case _ => println("invalid file format?") } } } } // 画像のフォーマット(jpeg, gif, png etc.) object imageFormat extends MappedString(this, 4) { override def dbDisplay_? = false } }
■おわりに
アップロードされた画像をDBに格納するところは以上で完了。後は、それを画面に表示させたい。それについては今後書く予定。この辺とか参考になる。