読者です 読者をやめる 読者になる 読者になる

Pseudo Engineer

ソフトウェアの話とか書いてくよ

Play Framework でArray[Byte]をそのままHTTP Responseで返す方法

データベースから引っ張ってきたBlobとかをそのままHTTPレスポンスでファイルとして返したいときとかありますよね。Playはローカルのファイルを指定する簡単な方法(Ok.sendFile)はあるのですが、Array[Byte]をbodyに入れたい場合、自力でCONTENT_LENGTHを設定したりとか、ちょっとめんどいですね。

なので、その辺簡単にできるようなコードを書いてみました。

import play.api.mvc.SimpleResult
import play.api.libs.iteratee.Enumerator
import play.api.http.HeaderNames

object current extends current

trait current {
  implicit class ByteContentResult(self: SimpleResult) {
    def withByteContent(contentType: String, filename: Option[String], content: Array[Byte]): SimpleResult = {
      val newHeader = self.header.copy(
        headers = self.header.headers
          ++ Map(HeaderNames.CONTENT_LENGTH -> content.length.toString, HeaderNames.CONTENT_TYPE -> contentType)
          ++ filename.map { fn =>
            Map(HeaderNames.CONTENT_DISPOSITION -> ("""attachment; filename="%s"""".format(fn)))
          }.getOrElse(Map.empty))
      SimpleResult(newHeader, Enumerator(content))
    }
  }
}

こう書くと、

object Application extends Controller {
  object DataStore {
    val filename = "hoge.gif"
    val contentType = "image/gif"
    val blobdata = "data".getBytes
  }

  def index = Action { request =>
    Ok.withByteContent(DataStore.contentType, Some(DataStore.filename), DataStore.blobdata)
  }
}

こんな感じで書けるようになります。implicit classすげー。


Githubにも置いておきます。
https://github.com/jetBeaver/ByteContentResult