从Play 2 Scala控制器提供静态/公共/文件

时间:2012-08-13 15:21:11

标签: scala playframework-2.0

Play Framework 2 Scala控制器提供静态文件的首选方法是什么?

该文件与我的应用程序捆绑在一起,因此无法对文件系统绝对/path/to/the/file进行硬编码,因为它的位置取决于Play应用程序恰好安装的位置。

该文件放在public / dir中,但不在app / assets /中,因为我不希望Play编译它。

(我之所以不简单地添加到该文件的路由,是因为在访问该文件之前需要登录,否则没有用。)

这是我到目前为止所做的,但这会在我的生产服务器上中断。

object Application ...

  def viewAdminPage = Action ... {
    ... authorization ...
    val adminPageFile = Play.getFile("/public/admin/index.html")
    Ok.sendFile(adminPageFile, inline = true)
  }

在我的routes文件中,我有这一行:

GET /-/admin/ controllers.Application.viewAdminPage

问题是在我的生产服务器上发生了这样的错误:
FileNotFoundException: app1/public/admin/index.html

是否还有其他方法(而不是Play.getFileOK.sendFile)来指定要投放的文件?生产中从未打破过?

(我的应用已安装在/some-dir/app1/,我是从/some-dir/启动的(没有app1/) - 如果我改为从/some-dir/app1/启动应用,那么一切都会有效。但我想知道一个“应该”如何做,从控制器内部提供一个静态文件?这样一切都可以在生产服务器上运行,无论我从哪里开始运行应用程序)

5 个答案:

答案 0 :(得分:3)

检查Streaming HTTP responses doc

def index = Action {
  Ok.sendFile(
    content = new java.io.File("/tmp/fileToServe.pdf"),
    fileName = _ => "termsOfService.pdf"
  )
}

您可以向fileName(每个已登录用户的个人)添加一些随机字符串,以避免在经过身份验证的用户和非经过身份验证的用户之间共享下载链接,还可以制作高级下载统计信息。

答案 1 :(得分:3)

我这样做了:(但请参阅下面的更新!)

val fileUrl: java.net.URL = this.getClass().getResource("/public/admin/file.html")
val file = new java.io.File(adminPageUrl.toURI())
Ok.sendFile(file, inline = true)

this是控制器,它(并且必须)与正在提供的文件位于同一个包中。)

以下是相关问题:open resource with relative path in java

更新

通过URI访问文件会导致错误:IllegalArgumentException: URI is not hierarchical,如果该文件位于JAR内部,如果您像这样运行Play,则会出现这种情况:play stage然后{{1 }}

因此,我将文件作为流读取,将其转换为String,并将该字符串作为HTML发送:

target/start

答案 2 :(得分:1)

手动方法如下:

val url = Play.resource(file)
url.map { url =>
  val stream = url.openStream()
  val length = stream.available 
  val resourceData = Enumerator.fromStream(stream)

  val headers = Map(
        CONTENT_LENGTH -> length.toString,
        CONTENT_TYPE -> MimeTypes.forFileName(file).getOrElse(BINARY),
        CONTENT_DISPOSITION -> s"""attachment; filename="$name"""")

  SimpleResult(
    header = ResponseHeader(OK, headers), 
    body = resourceData)

使用资产控制器的等价物如下:

val name = "someName.ext"

val response = Assets.at("/public", name)(request)
response
  .withHeaders(CONTENT_DISPOSITION -> s"""attachment; filename="$name"""")

答案 3 :(得分:1)

另一种变体,不使用String,而是通过流式传输文件内容:

def myStaticRessource() = Action { implicit request =>

  val contentStream = this.getClass.getResourceAsStream("/public/content.html")

  Ok.chunked(Enumerator.fromStream(contentStream)).as(HTML)

}

答案 4 :(得分:1)

播放具有内置方法:

  Ok.sendResource("public/admin/file.html", classLoader)

您可以使用Environment从注入的environment.classLoaderthis.getClass.getClassLoader中获取类加载器。