PlayFramework 2.3.x:使用带Play和Scala的URL访问公用文件夹

时间:2015-03-17 10:27:02

标签: scala playframework-2.3

我正在使用网络服务上传视频和图片,并将图片保存在我们的应用程序中。保存文件时,文件保存在应用程序文件夹的根目录下。我想使用localhost网址访问这些图片和视频,例如:我上传文件并保存在app-root/upload/image.jpg下。在我的路由映射文件中,我声明路由如下:

GET     /uploads/                                   staticDir:/upload 

Play Documentation中定义。但仍然遇到编译时错误:Controller method call expected。我想访问像http://localhost:9999/uploads/image.jpg

这样的图片

1 个答案:

答案 0 :(得分:0)

嗯......这样做的一种方法是添加以下路线,

GET   /uploads/*file        controllers.Assets.at(path="/uploads", file)

但是,它会干扰现有路线的反向路由,即

GET     /assets/*file               controllers.Assets.at(path="/public", file)

然后,您必须将这两条资产路线用作 - @route.Assets.at("public", filename)@route.Assets.at("uploads", filename),这意味着您使用公共资产路线的所有模板 - @route.Assets.at(filename)必须是改变。在现有的大项目中,这可能是一件麻烦事。

您可以使用以下方法来避免这种情况,

创建另一个控制器,     包控制器

object FileServer extends Controller {

  def serveUploadedFiles1 = controllers.Assets.at( dicrectoryPath, file, false )

  // Or... following is same as above

  def serveUploadedFiles2( file: String ) = Action.async {
    implicit request => {
      val dicrectoryPath = "/uploads"
      controllers.Assets.at( dicrectoryPath, file, false ).apply( request )
    }
  }

}

上面的内容应该有效......但是看起来好像Play对所请求的"资产"进行了大量的元数据检查。以某种方式导致所有/uploads/filename个请求的空结果。我试着查看播放源代码进行检查,但似乎可能需要一段时间来弄明白。

所以我认为我们可以使用以下更简单的方法(它可以在很多方面进一步完善。)。

object FileServer extends Controller {

  import play.api.http.ContentTypes
  import play.api.libs.MimeTypes
  import play.api.libs.iteratee.Enumerator
  import play.api.libs.concurrent.Execution.Implicits.defaultContext

  def serveUploadedFiles(file: String) = Action { implicit request =>
    val fileResUri = "uploads/"+file
    val mimeType: String = MimeTypes.forFileName( fileResUri ).fold(ContentTypes.BINARY)(addCharsetIfNeeded)
    val serveFile = new java.io.File(fileResUri)
    if( serveFile.exists() ){
      val fileContent: Enumerator[Array[Byte]] = Enumerator.fromFile( serveFile )
      //Ok.sendFile(serveFile).as( mimeType )
      val response = Result(
        ResponseHeader(
          OK,
          Map(
            CONTENT_LENGTH -> serveFile.length.toString,
            CONTENT_TYPE -> mimeType
          )
        ),
        fileContent
      )
      response
    }
    else {
      NotFound
    }
  }

  def addCharsetIfNeeded(mimeType: String): String =
    if (MimeTypes.isText(mimeType)) s"$mimeType; charset=$defaultCharSet" else mimeType

  lazy val defaultCharSet = config(_.getString("default.charset")).getOrElse("utf-8")

  def config[T](lookup: Configuration => Option[T]): Option[T] = for {
    app <- Play.maybeApplication
    value <- lookup(app.configuration)
  } yield value

}

但是这种方法会在打包构建部署的情况下造成一些麻烦。

这意味着,使用Play的Asset内容将是更明智的选择。再看一遍,controllers.Assets.at实际上controllers.Assets.assetAt在一个地方使用此方法,

def resource(name: String): Option[URL] = for {
  app <- Play.maybeApplication
  resource <- app.resource(name)
} yield resource

这意味着,它会尝试在属于应用程序classpath的目录中找到资源,而我们的uploads文件夹肯定不是其中之一。所以...我们可以通过将Assets.at添加到类路径来制作游戏的uploads功能。

但是......再想一想......如果我记得类路径中的所有文件夹都应该打包在要打包的包中,以便打包 - 构建部署。上传的内容将由用户创建,这意味着他们不应该成为包的一部分。这又意味着......我们不应该尝试使用Play的Assets.at内容来访问我们上传的内容。

所以......我认为我们最好使用我们自己更简单的基本实现serveUploadedFiles

现在在路径文件中添加路由为

GET         /uploads/*file                        controllers.FileServer.serveUploadedFiles( file:String )

另外......请记住,您不应该考虑使用Play来提供上传的资源。请使用nginx或类似的东西。