文件

§使用公開資產

在 Play 中提供公開資源與提供任何其他 HTTP 要求相同。它使用與常規資源相同的路由,使用控制器/動作路徑將 CSS、JavaScript 或影像檔案分發給客戶端。

§public/ 資料夾

依慣例,公開資產儲存在應用程式的 public 資料夾中。此資料夾可以按照您喜歡的方式組織。我們建議採用以下組織方式

public
 └ javascripts
 └ stylesheets
 └ images

如果您遵循此結構,將更容易入門,但一旦您了解其運作方式,就不會阻止您修改它。

§WebJars

WebJars 提供一個方便且傳統的封裝機制,這是 sbt 的一部分。例如,您可以透過在建置檔案中新增下列相依性,宣告您將使用熱門的 Bootstrap 函式庫

libraryDependencies += "org.webjars" % "bootstrap" % "3.3.6"

WebJars 會自動解壓縮到相對於您的公開資源的 lib 資料夾中,以方便使用。例如,如果您宣告相依於 RequireJs,則您可以使用類似下列的程式碼從檢視中參照它

<script data-main="@routes.Assets.at("javascripts/main.js")" type="text/javascript" src="@routes.Assets.at("lib/requirejs/require.js")"></script>

請注意 lib/requirejs/require.js 路徑。lib 資料夾表示解壓縮的 WebJar 資源,requirejs 資料夾對應到 WebJar artifactId,而 require.js 則參照 WebJar 根目錄中的必要資源。為了澄清,requirejs webjar 相依性會在您的建置檔案中宣告,如下所示

libraryDependencies += "org.webjars" % "requirejs" % "2.2.0"

§公開資源是如何封裝的?

在建置過程中,public 資料夾的內容會經過處理並新增到應用程式類別路徑。

當您封裝應用程式時,應用程式的所有資源,包括所有子專案,都會彙總到一個 jar 中,位於 target/my-first-app-1.0.0-assets.jar。此 jar 會包含在發行版中,以便您的 Play 應用程式可以提供服務。此 jar 也可用於將資源部署到 CDN 或反向代理程式。

§資源控制器

Play 內建一個控制器來提供公開資源服務。預設情況下,此控制器提供快取、ETag、gzip 和壓縮支援。資源控制器支援兩種不同的樣式:第一種是使用 Play 的組態,第二種是將資源路徑直接傳遞給控制器。

§繫結資源元件

如果您使用執行時期依賴性注入,Play 已在 AssetsModule 中提供繫結,此模組會預設載入。(如果您不使用資產,您可以透過新增組態 play.modules.disabled += controllers.AssetsModule 來停用此模組。)。此處的繫結會讓 Assets 類別可注入。

如果您使用元件特質來執行編譯時期依賴性注入,您應該混合 controllers.AssetsComponents。然後控制器將可用為 assets: Assets。您不需要自己建構控制器。

§使用資產與組態

對於最常見的情況,您只有一個放置資產的集中位置,您可以使用組態來指定位置

play.assets {
  path = "/public"
  urlPrefix = "/assets"
}

並使用具有單一參數的 Assets.at 方法

Assets.at(file: String)

然後在路由中

GET  /assets/*file        controllers.Assets.at(file)

§直接傳遞資產路徑

Assets 控制器也定義具有兩個參數的 at 動作

Assets.at(path: String, file: String)

path 參數必須是固定的,並定義動作管理的目錄。file 參數通常會動態從要求路徑中萃取。

以下是您 conf/routes 檔案中 Assets 控制器的一般對應

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

請注意,我們定義 *file 動態部分,它會符合 .* 正規表示式。因此,例如,如果您將此要求傳送至伺服器

GET /assets/javascripts/jquery.js

路由器會呼叫 Assets.at 動作,並附上下列參數

controllers.Assets.at("/public", "javascripts/jquery.js")

若要路由至單一靜態檔案,必須同時指定路徑和檔案

GET  /favicon.ico        controllers.Assets.at(path="/public", file="favicon.ico")

§反向路由用於公開資產

對於在路由檔案中對應的任何控制器,反向控制器會建立在 controllers.routes.Assets 中。您使用此反向控制器來反向取得公開資源所需的 URL。例如,從範本

<script src="@routes.Assets.at("javascripts/jquery.js")"></script>

DEV 模式中,這將預設產生下列結果

<script src="/assets/javascripts/jquery.js"></script>

如果您的應用程式未在 DEV 模式中執行,存在 jquery.min.jsjquery-min.js 檔案,則預設會使用壓縮檔案

<script src="/assets/javascripts/jquery.min.js"></script>

這讓 JavaScript 檔案在開發期間更容易除錯。當然,這不僅適用於 JavaScript 檔案,也適用於任何檔案副檔名。
如果您不希望 Play 自動解析 .min.*-min.* 檔案,不論您的應用程式在何種模式中執行,您可以在 application.conf 中設定 play.assets.checkForMinified = false(或設定為 true 以在任何模式中,甚至 DEV 模式中,始終解析壓縮檔案)。

請注意,當我們反向路由時,我們沒有指定第一個 folder 參數。這是因為我們的路由檔案為 Assets.at 動作定義單一對應,其中 folder 參數是固定的。因此不需要指定。

但是,如果您為 Assets.at 動作定義兩個對應,如下所示

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

那麼您在使用反向路由時需要指定兩個參數

<script src="@routes.Assets.at("/public/javascripts", "jquery.js")"></script>
<img src="@routes.Assets.at("/public/images", "logo.png")" />

§反向路由和公開資產的指紋辨識

sbt-web 將高度可配置的資產管線概念帶入 Play,例如在您的建置檔案中

pipelineStages := Seq(rjs, digest, gzip)

上述範例會依序執行 RequireJs 最佳化器 (sbt-rjs)、摘要 (sbt-digest),然後是壓縮 (sbt-gzip)。與許多 sbt 任務不同,這些任務會依據宣告的順序,一個接著一個執行。

資產指紋辨識的本質是讓您的靜態資產能以積極的快取指令提供給瀏覽器。這會改善使用者的體驗,因為後續造訪您的網站時,需要下載的資產會減少。Rails 也說明了 資產指紋辨識 的好處。

plugins.sbt 中宣告 pipelineStages 和必要的 addSbtPlugin 宣告,是您開始的起點。您必須向 Play 宣告哪些資產要進行版本控制。

有兩種方法可以取得指紋辨識資產的真實路徑。第一種方法使用靜態狀態,並支援與一般反向路由相同的樣式。它是透過檢視由執行中的 Play 應用程式設定的資產元資料來做到這一點。第二種方法是使用組態,並注入 AssetsFinder 來尋找您的資產。

§使用反向路由和靜態狀態

如果您計畫使用反向路由器和靜態狀態,下列路由檔案條目宣告所有資產都要進行版本控制

GET  /assets/*file  controllers.Assets.versioned(path="/public", file: Asset)

注意:請務必透過撰寫 file: Asset 來指出 file 是資產。

然後您使用反向路由器,例如在 scala.html 檢視中

<link rel="stylesheet" href="@routes.Assets.versioned("assets/css/app.css")">

此方法的缺點是,它需要特殊邏輯來將路徑中的 Asset 轉換為具有摘要的最終縮小路徑。由於沒有可以模擬來定義路徑的組件,因此單元測試也更困難。

§使用組態和 AssetsFinder

您也可以在組態中定義路徑,並將 AssetsFinder 注入到控制器中以取得最終路徑。在組態中設定資產 path(包含資產的目錄)和 urlPrefix(應用程式中 URL 的前置詞)

play.assets {
  path = "/public"
  urlPrefix = "/assets"
}

在路由檔案中,您可以定義路由如下

GET  /assets/*file        controllers.Assets.versioned(file)

(您不應該在此處使用 : Asset 型別註解)

然後,您可以將 AssetsFinder 傳遞給範本,並使用它來取得最終路徑

@(assetsFinder: AssetsFinder)

<link rel="stylesheet" href="@assetsFinder.path("assets/css/app.css")">

此方法的優點是不需要靜態狀態來設定。這表示您可以透過僅傳遞 AssetsFinder 的執行個體,在沒有執行中應用程式的狀況下單元測試控制器和範本。透過僅實作傳回 String 的抽象方法,可以輕鬆地模擬單元測試。

使用 AssetsFinder 方法也可以輕鬆地在同一個類別載入器中同時執行多個獨立的應用程式,因為它不使用任何靜態狀態。這對於測試也很有幫助。

AssetsFinder 介面在未使用指紋辨識的情況下也能運作。如果找不到指紋辨識和/或縮小的資產,它會傳回原始資產。

§Etag 支援

Assets 控制器會自動管理 ETag HTTP 標頭。ETag 值是由摘要(如果資產管線中使用 sbt-digest)或資源名稱和檔案的最後修改日期產生。如果資源檔案嵌入到檔案中,則會使用 JAR 檔案的最後修改日期。

當網路瀏覽器提出指定此 Etag 的要求時,伺服器可以使用 304 NotModified 回應。

§Gzip 支援

如果找到一個名稱相同但使用 .gz 字尾的資源,則 Assets 控制器也會提供後者並新增下列 HTTP 標頭

Content-Encoding: gzip

在您的建置中包含 sbt-gzip 外掛並在 pipelineStages 中宣告其位置,即可產生 gzip 檔案。

§其他 Cache-Control 指令

使用 Etag 通常足以用於快取目的。不過,如果您想為特定資源指定自訂 Cache-Control 標頭,您可以在 application.conf 檔案中指定它。例如

# Assets configuration
# ~~~~~
play.assets.cache."/public/stylesheets/bootstrap.min.css"="max-age=3600"

您也可以使用部分路徑為在該路徑下的所有資產指定自訂 Cache-Control,例如

# Assets configuration
# ~~~~~
play.assets.cache."/public/stylesheets/"="max-age=100"
play.assets.cache."/public/javascripts/"="max-age=200"

Play 會對 /public/javascripts 下的所有資產(例如 /public/javascripts/main.js)使用 max-age=200,並對 /public/stylesheets 下的所有資產(例如 /public/stylesheets/main.css)使用 max-age=100

§其他指令的套用方式

Play 會依序對 Cache-Control 指令進行詞彙排序,並從較具體到較不具體的順序排序。例如,針對下列組態

# Assets configuration
# ~~~~~
play.assets.cache."/public/stylesheets/"="max-age=101"
play.assets.cache."/public/stylesheets/layout/"="max-age=102"
play.assets.cache."/public/stylesheets/app/"="max-age=103"
play.assets.cache."/public/stylesheets/layout/main.css"="max-age=103"

play.assets.cache."/public/javascripts/"="max-age=201"
play.assets.cache."/public/javascripts/app/"="max-age=202"
play.assets.cache."/public/javascripts/app/main.js"="max-age=203"

指令會依序排序並套用,如下所示

play.assets.cache."/public/javascripts/app/main.js"="max-age=203"
play.assets.cache."/public/javascripts/app/"="max-age=202"
play.assets.cache."/public/javascripts/"="max-age=201"

play.assets.cache."/public/stylesheets/app/"="max-age=103"
play.assets.cache."/public/stylesheets/layout/main.css"="max-age=103"
play.assets.cache."/public/stylesheets/layout/"="max-age=102"
play.assets.cache."/public/stylesheets/"="max-age=101"

注意:類似 play.assets.cache."/public/stylesheets"="max-age=101" 的組態會同時符合 public/stylesheets.csspublic/stylesheets/main.css,因此您可能想新增一個尾隨的 / 以便更清楚區分目錄,例如 play.assets.cache."/public/stylesheets/"="max-age=101"

§受控資產

從 Play 2.3 開始,受控的資源由 sbt-web 基礎外掛處理。在 2.3 之前,Play 以 CoffeeScript、LESS、JavaScript linting(ClosureCompiler)和 RequireJS 最佳化等形式,將受控資源處理打包在一起。下列各節說明 sbt-web,以及如何達成等同於 2.2 的功能。但請注意,Play 並不限於這項資源處理技術,因為隨著時間的推移,許多外掛應該都會提供給 sbt-web。請查看 sbt-web 專案,以進一步了解有哪些外掛可用。

許多外掛使用 sbt-web 的 js-engine 外掛。js-engine 能夠執行寫入 Node API 的外掛,透過優秀的 Trireme 專案在 JVM 內執行,或直接在 Node.js 上執行以提升效能。請注意,這些工具僅在開發週期中使用,在 Play 應用程式的執行期間不會介入。如果您已安裝 Node.js,建議您宣告下列環境變數。對於 Unix,如果已在其他地方定義了 SBT_OPTS,則您可以

export SBT_OPTS="$SBT_OPTS -Dsbt.jse.engineType=Node"

上述宣告可確保在執行任何 sbt-web 外掛時,都會使用 Node.js。

§範圍要求支援

Assets 控制器自動支援 RFC 7233 的一部分,其中定義了範圍要求和部分回應的運作方式。如果要求中存在可滿足的 Range 標頭,Assets 控制器將傳送 206 Partial Content。它也會為所有資源傳送回 Accept-Ranges: bytes

注意:除了已進行一些剖析以更好地處理多重範圍之外,multipart/byteranges 尚未完全支援。

您也可以在不使用 Assets 控制器的情況下傳回 206 Partial Content,以傳送檔案

§Scala 版本

def video(videoId: Long): Action[AnyContent] = Action { implicit request =>
  val videoFile = getVideoFile(videoId)
  RangeResult.ofFile(videoFile, request.headers.get(RANGE), Some("video/mp4"))
}

§Java 版本

public Result video(Http.Request request, Long videoId) {
  File videoFile = getVideoFile(videoId);
  return RangeResults.ofFile(request, videoFile);
}

這兩個範例都會根據所要求的範圍,僅傳送影片檔案的一部分。

下一頁:使用 CoffeeScript


在這個文件裡發現錯誤了嗎?此頁面的原始碼可以在 這裡 找到。在閱讀完 文件指南 之後,請隨時貢獻一個 pull request。有問題或建議要分享嗎?請前往 我們的社群論壇,與社群展開對話。