4

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

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

该文件放在 public/ 目录中,但不在 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.getFileandOK.sendFile来指定要提供的文件?那在生产中永远不会中断?

(我的应用程序安装在/some-dir/app1/,我从/some-dir/(没有app1/)启动它 - 如果我改为从 启动应用程序,也许一切都会工作/some-dir/app1/。但我想知道一个“应该”如何从控制器内部提供静态文件?所以无论我碰巧从哪里启动应用程序,一切都在生产服务器上也能正常工作)

4

5 回答 5

3

检查流式 HTTP 响应文档

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

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

于 2012-08-13T18:54:36.943 回答
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然后target/start.

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

val adminPageFileString: String = {
  // In prod builds, the file is embedded in a JAR, and accessing it via
  // an URI causes an IllegalArgumentException: "URI is not hierarchical".
  // So use a stream instead.
  val adminPageStream: java.io.InputStream =
    this.getClass().getResourceAsStream("/public/admin/index.html")
  io.Source.fromInputStream(adminPageStream).mkString("")
}

...

return Ok(adminPageFileString) as HTML
于 2012-08-14T06:00:38.003 回答
2

Play 有一个内置的方法:

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

Environment您可以从注入的 withenvironment.classLoader或 from获取类加载器this.getClass.getClassLoader

于 2019-03-10T13:18:56.127 回答
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"""")
于 2013-06-18T12:34:50.490 回答
1

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

def myStaticRessource() = Action { implicit request =>

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

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

}
于 2015-06-07T06:05:15.397 回答