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に格納するところは以上で完了。後は、それを画面に表示させたい。それについては今後書く予定。この辺とか参考になる。

コメントを残す

メールアドレスが公開されることはありません。