2

Play Framework bundles static assets into a JAR bundle which is used at runtime to resolve (via classpath lookup) requested assets. I would like, instead, to specify a filesystem location where these static assets should be served from. I found a hacky way to do this (see below), but it seems like there should be an easier/better way to do this (although the documentation doesn't mention it).

I know that I can create a simple controller and "manually" serve the assets, but this doesn't take advantage of the built-in caching and versioning capabilities provided by the standard Assets mechanism.

(my additional requirement is that I want to have some assets private, i.e. to require authentication/authorization to be able to access them)

So here's my current hacky solution:

import java.net.URL
import java.nio.file.{Files, Paths}

import controllers._
import javax.inject.Inject
import play.api.http.{FileMimeTypes, HttpErrorHandler}
import play.api.mvc.{Action, AnyContent, ControllerComponents}
import play.api.{Configuration, Environment}

import scala.concurrent.ExecutionContext

object AssetResolver {
  def resolve(path: String): Option[URL] = {
    val resPath = Paths.get(path)
    if (Files.exists(resPath))
      Some(resPath.toUri.toURL)
    else None
  }
}

class AssetsController @Inject()(config: Configuration,
                                 env: Environment,
                                 httpErrorHandler: HttpErrorHandler,
                                 fileMimeTypes: FileMimeTypes)
                                (implicit ec: ExecutionContext) {
  private val configuration = AssetsConfiguration.fromConfiguration(config, env.mode)
  private val assetsMetadata = new DefaultAssetsMetadata(configuration, AssetResolver.resolve _, fileMimeTypes)
  private val assets = new Assets(httpErrorHandler, assetsMetadata)

  // authentication checking logic removed since irrelevant to question
  def secureAt(file: String): Action[AnyContent] = assets.at(file)
}

Then, in conf/application.conf one can define:

play.assets {
    path = "/absolute/path/to/assets"   # *must* be an absolute path
    urlPrefix = "/assets"
}

and then in conf/routes the controller can be used as follows:

GET /assets/*file   controllers.AssetsController.secureAt(file)

Is there a better way to do this? (I'm using Play 2.7)

borice
  • 1,009
  • 1
  • 8
  • 15

0 Answers0