静的ファイルの提供

アプリケーションでは、動的リクエストを処理するほか、JavaScript、画像、CSS などの静的ファイルを提供する必要が生じることがよくあります。フレキシブル環境のアプリで静的ファイルを提供する方法として、Cloud Storage などの Google Cloud のサービスを使用する、アプリから直接提供する、サードパーティのコンテンツ配信ネットワーク(CDN)を使用するなどがあります。

Cloud Storage からファイルを提供する

Cloud Storage では、動的ウェブアプリの静的アセットをホストできます。アプリから直接提供する代わりに Cloud Storage を使用すると、次のメリットがあります。

  • Cloud Storage は基本的に、コンテンツ配信ネットワークとして機能します。一般公開されているすべてのオブジェクトがデフォルトでグローバル Cloud Storage ネットワークのキャッシュに保存されるため、特別な構成は必要ありません。
  • 静的アセットの提供を Cloud Storage にオフロードすることで、アプリの負荷が軽減されます。静的アセットの数とアクセスの頻度によっては、アプリの実行コストを大幅に削減できる可能性があります。
  • コンテンツにアクセスするための帯域幅の料金は、多くの場合、Cloud Storage を利用すると低くなります。

アセットを Cloud Storage にアップロードするには、gsutil コマンドライン ツールまたは Cloud Storage API を使用します。

Google Cloud クライアント ライブラリは、汎用的なクライアントを Cloud Storage に提供します。これにより、App Engine アプリで Cloud Storage を使用してデータの格納や取得を行うことができます。

Cloud Storage バケットから提供する例

Cloud Storage バケットを作成し、Google Cloud CLI を使用して静的なアセットをアップロードする簡単な例を示します。

  1. バケットを作成します。プロジェクト ID の後にバケットに名前を付けるのは一般的ですが必須ではありません。バケット名はグローバルに一意でなければなりません。

    gsutil mb gs://<your-bucket-name>
    
  2. バケット内のアイテムへの読み取りアクセス権を付与するよう ACL を設定します。

    gsutil defacl set public-read gs://<your-bucket-name>
    
  3. アイテムをバケットにアップロードします。通常は、rsync コマンドを使用すると、最も短時間で簡単にアセットをアップロードして更新できます。cp も使用できます。

    gsutil -m rsync -r ./static gs://<your-bucket-name>/static
    

これで、https://storage.googleapis.com/<your-bucket-name>/static/... を介して静的アセットにアクセスできるようになりました。

カスタム ドメイン名から提供する方法など、Cloud Storage を使用して静的アセットを提供する方法の詳細については、静的ウェブサイトのホスティングをご覧ください。

他の Google Cloud サービスからファイルを提供する

Cloud CDN または他の Google Cloud ストレージ サービスを使用することもできます。

アプリからファイルを直接提供する

通常、アプリからファイルを提供するのに複雑な設定は必要ありませんが、考慮すべき弱点がいくつかあります。

  • 静的ファイルのリクエストにより、動的リクエストに使用できるはずのリソースが消費される可能性があります。
  • 構成によっては、アプリからファイルを提供する際にレスポンスのレイテンシが生じることがあります。負荷を処理するために新しいインスタンスを作成する場合にも、この影響を受けることがあります。

アプリで静的ファイルを提供する例

Go

次のサンプルは、バージョン 1.15 以前およびバージョン 1.18 以降の Go ランタイムのアプリで静的ファイルを提供する方法を示しています。新しいバージョンを使用するには app.yaml を更新する必要があります。新しいランタイムの使用の詳細については、Go ランタイムをご覧ください。

標準の http.FileServer または http.ServeFile を使用して、アプリから直接ファイルを提供できます。

v1.18 以降


// Package static demonstrates a static file handler for App Engine flexible environment.
package main

import (
	"fmt"
	"net/http"

	"google.golang.org/appengine"
)

func main() {
	// Serve static files from "static" directory.
	http.Handle("/static/", http.FileServer(http.Dir(".")))

	http.HandleFunc("/", homepageHandler)
	appengine.Main()
}

const homepage = `<!doctype html>
<html>
<head>
  <title>Static Files</title>
  <link rel="stylesheet" type="text/css" href="/static/main.css">
</head>
<body>
  <p>This is a static file serving example.</p>
</body>
</html>`

func homepageHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, homepage)
}

v1.15 以前


// Package static demonstrates a static file handler for App Engine flexible environment.
package main

import (
	"fmt"
	"net/http"

	"google.golang.org/appengine"
)

func main() {
	// Serve static files from "static" directory.
	http.Handle("/static/", http.FileServer(http.Dir(".")))

	http.HandleFunc("/", homepageHandler)
	appengine.Main()
}

const homepage = `<!doctype html>
<html>
<head>
  <title>Static Files</title>
  <link rel="stylesheet" type="text/css" href="/static/main.css">
</head>
<body>
  <p>This is a static file serving example.</p>
</body>
</html>`

func homepageHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, homepage)
}

Java

次のサンプルは、Java ランタイム バージョン 8 とバージョン 11 / 17 のアプリで静的ファイルを提供する方法を示しています。新しいバージョンを使用するには app.yaml を更新する必要があります。新しいランタイムの使用の詳細については、Java ランタイムをご覧ください。

Java ランタイムのサーブレット コンテナはアプリのデプロイ記述子web.xml ファイル)を使用して、静的アセットを含むサーブレットに URL をマッピングします。web.xml を指定しない場合はデフォルトが使用され、デフォルト サーブレットにすべてがマッピングされます。

この例で、./src/main/webapp/index.html/stylesheets/styles.css から提供されるスタイルシートを参照します。

バージョン 11 / 17

<!doctype html>
<html>
<head>
<title>Static Files</title>
<link rel="stylesheet" type="text/css" href="/stylesheets/styles.css">
</head>
<body>
  <p>This is a static file serving example.</p>
</body>
</html>

バージョン 8

<!doctype html>
<html>
<head>
<title>Static Files</title>
<link rel="stylesheet" type="text/css" href="/stylesheets/styles.css">
</head>
<body>
  <p>This is a static file serving example.</p>
</body>
</html>

styles.css ファイルは ./src/main/webapp/stylesheets/styles.css にあります。

body {
  font-family: Verdana, Helvetica, sans-serif;
  background-color: #CCCCFF;
}

web.xml ファイルで静的ファイルの処理方法を明示的に構成できます。たとえば、拡張子が .jpg になっているすべてのファイルのリクエストをマッピングするには、次のように設定します。

<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.jpg</url-pattern>
</servlet-mapping>

Play などのウェブ フレームワークを使用している場合は、静的アセットについてフレームワークのドキュメントを参照する必要があります。

Node.js

次のサンプルは、バージョン 16 以前およびバージョン 18 以降の Node.js ランタイムのアプリで静的ファイルを提供する方法を示しています。新しいバージョンを使用するには app.yaml を更新する必要があります。新しいランタイムの使用の詳細については、Node.js ランタイムをご覧ください。

ほとんどのウェブ フレームワークで、静的ファイルの提供がサポートされています。このサンプルでは、アプリケーションは express.static ミドルウェアを使用して ./public ディレクトリ内のファイルを /static URL に提供しています。

'use strict';

const express = require('express');
const app = express();

app.set('view engine', 'pug');

// Use the built-in express middleware for serving static files from './public'
app.use('/static', express.static('public'));

app.get('/', (req, res) => {
  res.render('index');
});

// Start the server
const PORT = parseInt(process.env.PORT) || 8080;
app.listen(PORT, () => {
  console.log(`App listening on port ${PORT}`);
  console.log('Press Ctrl+C to quit.');
});

ビューは /static/main.css を参照しています。

doctype html
html(lang="en")
  head
    title Static Files
    meta(charset='utf-8')
    link(rel="stylesheet", href="/static/main.css")
  body
    p This is a static file serving example.

スタイルシート自体は ./public/css にあり、/static/main.css から提供されます。

body {
  font-family: Verdana, Helvetica, sans-serif;
  background-color: #CCCCFF;
}

通常は、HapiKoaSails など他の Node.js フレームワークがアプリケーションからの静的ファイルの直接提供をサポートします。静的コンテンツを構成して使用する方法の詳細については、各フレームワークのドキュメントをご覧ください。

PHP

PHP ランタイムはアプリを処理するために nginx を実行します。これはプロジェクト ディレクトリ内の静的ファイルを提供するように構成されています。app.yaml ファイルに document_root を指定してドキュメント ルートを宣言する必要があります。

runtime: php
env: flex

runtime_config:
  document_root: web

Python

次のサンプルは、Python ランタイム バージョン 3.7 以前のアプリで静的ファイルを提供する方法を示しています。Python バージョン 3.8 以降の新しいバージョンの使用方法については、Python ランタイムをご覧ください。

ほとんどのウェブ フレームワークで、静的ファイルの提供がサポートされています。このサンプルでは、アプリが Flask の組み込み機能を使用して ./static ディレクトリから /static URL にファイルを提供します。

このアプリには、テンプレートを表示するビューが含まれています。Flask は、追加の構成なしで ./static ディレクトリ内のすべての内容を自動的に提供します。

import logging

from flask import Flask, render_template

app = Flask(__name__)

@app.route("/")
def hello():
    """Renders and serves a static HTML template page.

    Returns:
        A string containing the rendered HTML page.
    """
    return render_template("index.html")

@app.errorhandler(500)
def server_error(e):
    """Serves a formatted message on-error.

    Returns:
        The error message and a code 500 status.
    """
    logging.exception("An error occurred during a request.")
    return (
        f"An internal error occurred: <pre>{e}</pre><br>See logs for full stacktrace.",
        500,
    )

if __name__ == "__main__":
    # This is used when running locally. Gunicorn is used to run the
    # application on Google App Engine. See entrypoint in app.yaml.
    app.run(host="127.0.0.1", port=8080, debug=True)

ビューにより表示されるテンプレートには、/static/main.css にあるスタイルシートが組み込まれています。

<!doctype html>
<html>
<head>
  <title>Static Files</title>
  <!--
  Flask automatically makes files in the 'static' directory available via
  '/static'.
  -->
  <link rel="stylesheet" type="text/css" href="/static/main.css">
</head>
<body>
  <p>This is a static file serving example.</p>
</body>
</html>

スタイルシートは ./static/main.css にあります。

body {
  font-family: Verdana, Helvetica, sans-serif;
  background-color: #CCCCFF;
}

通常、DjangoPyramidBottle などの他の Python フレームワークでは、静的ファイルをアプリから直接提供できます。静的コンテンツを構成して使用する方法の詳細については、各フレームワークのドキュメントをご覧ください。

Ruby

ほとんどのウェブ フレームワークで、静的ファイルの提供がサポートされています。次のサンプルは、バージョン 3.1 以前およびバージョン 3.2 の Ruby ランタイムのアプリで静的ファイルを提供する方法を示しています。新しいバージョンを使用するには app.yaml ファイルを更新する必要があることに留意してください。新しいランタイムの使用の詳細については、Ruby ランタイムをご覧ください。

Sinatra

Sinatra ウェブ フレームワークのデフォルトでは、./public ディレクトリからファイルが提供されます。このアプリには、/application.css を参照するビューが含まれています。

body {
  font-family: Verdana, Helvetica, sans-serif;
  background-color: #CCCCFF;
}

スタイルシートは ./public/application.css にあり、/application.css から提供されます。

Ruby on Rails

Ruby on Rails ウェブ フレームワークは、デフォルトで ./public ディレクトリからファイルを提供します。静的 JavaScript ファイルと CSS ファイルは、Rails アセット パイプラインからも生成されます。

以下のサンプルアプリには、すべてのアプリケーション スタイルシートを含むレイアウト ビューが含まれています。

バージョン 3.2

doctype html
html
  head
    title Serving Static Files
    link rel="stylesheet" href="/application.css"
    script src="/application.js"
  body
    p This is a static file serving example.

バージョン 3.1 以前

doctype html
html
  head
    title Serving Static Files
    = stylesheet_link_tag "application", media: "all"
    = javascript_include_tag "application"
    = csrf_meta_tags
  body
    = yield

バージョン 3.2

スタイルシート自体は、./public/application.css にある .css ファイルです。

body {
  font-family: Verdana, Helvetica, sans-serif;
  background-color: #CCCCFF;
}

バージョン 3.1 以前

スタイルシート自体は、./app/assets/stylesheets/main.css.sass にある Sass ファイルです。

body
  font-family: Verdana, Helvetica, sans-serif
  background-color: #CCCCFF

Rails アプリが本番環境で実行されている場合、デフォルトでは、これらのアプリから静的アセットが生成または提供されません。

デプロイ時に、Ruby ランタイムrake assets:precompile を実行して静的アセットを生成し、RAILS_SERVE_STATIC_FILES 環境変数を設定します。これにより、本番環境での静的ファイルの提供が可能になります。

.NET

次のサンプルは、.NET ランタイム バージョン 3.1 以前とバージョン 6 以降のアプリで静的ファイルを提供する方法を示しています。新しいバージョンを使用するには app.yaml ファイルを更新する必要があることに留意してください。新しいランタイムの使用の詳細については、.NET ランタイムをご覧ください。

バージョン 6 以降

<html>
<head>
    <meta charset="utf-8" />
    <title>Hello Static World</title>
</head>
<body>
    <p>This is a static html document.</p>
    <p><img src="trees.jpg" /></p>
</body>
</html>

静的ファイルの提供を有効にするには、以下を追加します。

app.UseDefaultFiles();
app.UseStaticFiles();

バージョン 3.1 以前

<html>
<head>
    <meta charset="utf-8" />
    <title>Hello Static World</title>
</head>
<body>
    <p>This is a static html document.</p>
    <p><img src="trees.jpg" /></p>
</body>
</html>

静的ファイルの提供を有効にするには、以下を追加します。

app.UseDefaultFiles();
app.UseStaticFiles();

サードパーティのコンテンツ配信ネットワークから提供する

外部のサードパーティ CDN を使用して静的ファイルを提供し、動的リクエストをキャッシュに保存することはできますが、その場合、アプリのレイテンシとコストが増える可能性があります。

パフォーマンスを向上させるには、CDN Interconnect をサポートしているサードパーティ CDN を使用してください。