コンテンツにスキップ

Workers KVを使用して静的アセットを保存および取得する

Workers KVを使用して静的アセットを保存および取得する

静的アセットをWorkers KVに保存することで、これらのアセットを低遅延かつ高スループットでグローバルに取得できます。これらのアセットを直接提供することも、動的に応答を生成するために使用することもできます。これは、ファイルや画像を提供する場合や、翻訳などの静的アセットから動的なHTML応答を生成する場合に便利です。

Before you start

All of the tutorials assume you have already completed the Get started guide, which gets you set up with a Cloudflare Workers account, C3, and Wrangler.

1. 新しいWorkerアプリケーションを作成する

始めるには、create-cloudflare CLIを使用してWorkerアプリケーションを作成します。ターミナルウィンドウを開き、次のコマンドを実行します。

Terminal window
npm create cloudflare@latest -- example-kv-assets

For setup, select the following options:

  • For What would you like to start with?, choose Hello Worldの例.
  • For Which template would you like to use?, choose Hello World Worker.
  • For Which language do you want to use?, choose TypeScript.
  • For Do you want to use git for version control?, choose Yes.
  • For Do you want to deploy your application?, choose No (we will be making some changes before deploying).

次に、新しく作成したアプリケーションに移動します。

Terminal window
cd example-kv-assets

このプロジェクトに必要な依存関係もインストールします。

Terminal window
npm install mimes accept-language-parser
npm install --save-dev @types/accept-language-parser

2. 新しいKV名前空間を作成する

次に、KVストアを作成します。これはCloudflareダッシュボードまたはWrangler CLIを通じて行うことができます。この例では、Wrangler CLIを使用します。

Wranglerを介してKVストアを作成するには:

  1. ターミナルを開き、次のコマンドを実行します:

    Terminal window
    npx wrangler kv namespace create assets

    wrangler kv namespace create assetsサブコマンドは、Workerの名前とassetsに提供された値を連結してKV名前空間を作成します。KV名前空間のidはランダムに生成されます。

    Terminal window
    npx wrangler kv namespace create assets
    🌀 "example-kv-assets-assets"というタイトルの名前空間を作成中
    成功!
    kv_namespaces配列の設定ファイルに次の内容を追加してください:
    [[kv_namespaces]]
    binding = "assets"
    id = "<GENERATED_NAMESPACE_ID>"
  2. wrangler.tomlファイルに、ターミナルで生成された値を使用して次の内容を追加します:

    wrangler.toml
    [[kv_namespaces]]
    binding = "assets"
    id = "<GENERATED_NAMESPACE_ID>"

    KVバインディング assetsは、WorkerがKV名前空間と対話する方法です。このバインディングは、WorkersランタイムによってWorkersコード内のランタイム変数として提供されます。

    プレビューKV名前空間も作成します。ローカルで開発する際に本番名前空間に変更を加えないように、別のKV名前空間を作成することをお勧めします。リモートリソースに対してローカルで開発する場合、Wrangler CLIはwrangler.tomlファイルのKV名前空間設定内のpreview_idで指定された名前空間のみを使用します。

  3. ターミナルで次のコマンドを実行します:

    Terminal window
    npx wrangler kv namespace create assets --preview

    このコマンドは、リモートリソースに対してWranglerで開発する際にのみ使用される特別なKV名前空間を作成します。

    Terminal window
    npx wrangler kv namespace create assets --preview
    🌀 "example-kv-assets-assets_preview"というタイトルの名前空間を作成中
    成功!
    kv_namespaces配列の設定ファイルに次の内容を追加してください:
    [[kv_namespaces]]
    binding = "assets"
    preview_id = "<GENERATED_PREVIEW_NAMESPACE_ID>"
  4. wrangler.tomlファイルに、ターミナルで生成された値を使用してkv_namespacesの下に追加のpreview_idを追加します:

    wrangler.toml
    [[kv_namespaces]]
    binding = "assets"
    id = "<GENERATED_NAMESPACE_ID>"
    preview_id = "<GENERATED_PREVIEW_NAMESPACE_ID>"

これで、デプロイ時に本番KV名前空間を使用し、リモートリソースに対してローカルで開発する際にプレビューKV名前空間を使用する1つのKVバインディングができました。

3. Wranglerを使用してKVに静的アセットを保存する

静的アセットをKVに保存するには、Wrangler CLI、WorkerアプリケーションからのKVバインディング、またはKV REST APIを使用できます。ここでは、Wrangler CLIの使用方法を示します。

このシナリオでは、サンプルHTMLファイルをKVストアに保存します。プロジェクトのルートにindex.htmlという新しいファイルを作成し、次の内容を追加します:

index.html
Hello World!

次に、次のWranglerコマンドを使用して、このファイルのKVペアを本番およびプレビュー名前空間内に作成できます:

Terminal window
npx wrangler kv key put index.html --path index.html --binding assets --preview false
npx wrangler kv key put index.html --path index.html --binding assets --preview

これにより、ファイル名をキー、ファイル内容を値とするKVペアが、wrangler.tomlファイル内のバインディングで指定された本番およびプレビュー名前空間内に作成されます。

4. WorkerアプリケーションからKVの静的アセットを提供する

Workerプロジェクトのindex.tsファイル内の内容を次のように置き換えます:

index.ts
import mime from 'mime';
interface Env {
assets: KVNamespace;
}
export default {
async fetch(request, env, ctx): Promise<Response> {
// GETリクエストでない場合はエラーを返す
if(request.method !== 'GET'){
return new Response('Method Not Allowed', {
status: 405,
})
}
// URLからキーを取得し、キーが欠けている場合はエラーを返す
const parsedUrl = new URL(request.url)
const key = parsedUrl.pathname.replace(/^\/+/, '') // 前の/を削除
if(!key){
return new Response('Missing path in URL', {
status: 400
})
}
// キーパスからMIMEタイプを取得
const extension = key.split('.').pop();
let mimeType = mime.getType(key) || "text/plain";
if (mimeType.startsWith("text") || mimeType === "application/javascript") {
mimeType += "; charset=utf-8";
}
// KVストアから値を取得し、見つかった場合は返す
const value = await env.assets.get(key, 'arrayBuffer')
if(!value){
return new Response("Not found", {
status: 404
})
}
return new Response(value, {
status: 200,
headers: new Headers({
"Content-Type": mimeType
})
});
},
} satisfies ExportedHandler<Env>;

このコードは、URL内のパスを使用してKVストア内のパスに関連付けられたファイルを見つけます。また、応答内で適切なMIMEタイプを設定して、ブラウザに応答の処理方法を示します。KVストアから値を取得するために、このコードはarrayBufferを使用して、画像、文書、ビデオ/オーディオファイルなどのバイナリデータを適切に処理します。

Workerを開始するには、ターミナル内で次のコマンドを実行します:

Terminal window
npx wrangler dev --remote

これにより、リモートリソースに対してWorkerコードが実行され、特に構成されたプレビューKV名前空間が使用されます。

Terminal window
npx wrangler dev --remote
あなたのWorkerは次のバインディングにアクセスできます:
- KV名前空間:
- assets: <GENERATED_PREVIEW_NAMESPACE_ID>
[wrangler:inf] http://localhost:<PORT>で準備完了

Wranglerコマンドによって提供されたURLにアクセスすると、index.htmlファイルの内容が含まれたHTMLファイルが返されるのが確認できます。画像や文書で試してみると、このWorkerがKVからそれらのアセットを適切に提供していることがわかります。

5. キー-バリュー ペアから動的応答を生成するエンドポイントを作成する

KVに保存されたアセットから動的応答を生成する方法を示すために、Workersアプリケーションにhello-worldエンドポイントを追加します。このエンドポイントは、要求された言語に基づいて「Hello World!」メッセージを返します。

プロジェクトのルートに次のファイルを作成します:

hello-world.json
[
{
"language_code": "en",
"message": "Hello World!"
},
{
"language_code": "es",
"message": "¡Hola Mundo!"
},
{
"language_code": "fr",
"message": "Bonjour le monde!"
},
{
"language_code": "de",
"message": "Hallo Welt!"
},
{
"language_code": "zh",
"message": "你好,世界!"
},
{
"language_code": "ja",
"message": "こんにちは、世界!"
},
{
"language_code": "hi",
"message": "नमस्ते दुनिया!"
},
{
"language_code": "ar",
"message": "مرحبا بالعالم!"
}
]

ターミナルを開き、次のKVコマンドを入力して翻訳ファイルのKVエントリを作成します:

Terminal window
npx wrangler kv key put hello-world.json --path hello-world.json --binding assets --preview false
npx wrangler kv key put hello-world.json --path hello-world.json --binding assets --preview

Workersコードを更新して、リクエストのAccept-Languageヘッダーの言語に基づいて翻訳されたHTMLファイルを提供するロジックを追加します:

index.ts
import mime from 'mime';
import parser from 'accept-language-parser'
interface Env {
assets: KVNamespace;
}
export default {
async fetch(request, env, ctx): Promise<Response> {
// GETリクエストでない場合はエラーを返す
if(request.method !== 'GET'){
return new Response('Method Not Allowed', {
status: 405,
})
}
// URLからキーを取得し、キーが欠けている場合はエラーを返す
const parsedUrl = new URL(request.url)
const key = parsedUrl.pathname.replace(/^\/+/, '') // 前の/を削除
if(!key){
return new Response('Missing path in URL', {
status: 400
})
}
// 翻訳パスのハンドラーを追加
if(key === 'hello-world'){
// リクエストから言語ヘッダーを取得し、KVから翻訳を取得
const languageHeader = request.headers.get('Accept-Language') || 'en'//デフォルトは英語
const translations : {
"language_code": string,
"message": string
}[] = await env.assets.get('hello-world.json', 'json') || [];
// 要求された言語を抽出
const supportedLanguageCodes = translations.map(item => item.language_code)
const languageCode = parser.pick(supportedLanguageCodes, languageHeader, {
loose: true
})
// 選択された言語のメッセージを取得
let selectedTranslation = translations.find(item => item.language_code === languageCode)
if(!selectedTranslation) selectedTranslation = translations.find(item => item.language_code === "en")
const helloWorldTranslated = selectedTranslation!['message'];
// 翻訳されたHTMLを生成して返す
const html = `<!DOCTYPE html>
<html>
<head>
<title>Hello World翻訳</title>
</head>
<body>
<h1>${helloWorldTranslated}</h1>
</body>
</html>
`
return new Response(html, {
status: 200,
headers: {
'Content-Type': 'text/html; charset=utf-8'
}
})
}
// キーパスからMIMEタイプを取得
const extension = key.split('.').pop();
let mimeType = mime.getType(key) || "text/plain";
if (mimeType.startsWith("text") || mimeType === "application/javascript") {
mimeType += "; charset=utf-8";
}
// KVストアから値を取得し、見つかった場合は返す
const value = await env.assets.get(key, 'arrayBuffer')
if(!value){
return new Response("Not found", {
status: 404
})
}
return new Response(value, {
status: 200,
headers: new Headers({
"Content-Type": mimeType
})
});
},
} satisfies ExportedHandler<Env>;

この新しいコードは、特定のエンドポイント/hello-worldを提供し、翻訳された応答を提供します。このURLにアクセスすると、Workerコードは最初にクライアントがAccept-Languageリクエストヘッダーで要求した言語とKVストアからの翻訳を取得します。次に、翻訳されたメッセージを取得し、生成されたHTMLを返します。

Terminal window
npx wrangler dev --remote

Workerコードが実行されている状態で、アプリケーションが適切に翻訳された「Hello World」メッセージを返していることがわかります。ブラウザの開発者コンソールからロケール言語を変更してみてください(Chromiumブラウザでは、Show Sensorsを実行してロケールの選択ドロップダウンを取得できます)。

6. プロジェクトをデプロイする

wrangler deployを実行して、WorkersプロジェクトをCloudflareにデプロイし、KV名前空間へのバインディングを設定します。

Terminal window
npx wrangler deploy

Wranglerは自動的にKVバインディングを設定し、wrangler.tomlファイルに設定されたKV名前空間IDを使用して本番KV名前空間を使用します。この例では、アセットをプレビューおよび本番KV名前空間の両方にアップロードしました。

プロジェクトが正しく動作していることを確認するために、Workersのデフォルトホスト名にアクセスし、<WORKER-SUBDOMAIN>.<DEFAULT-ACCOUNT-HOSTNAME>.dev/index.htmlまたは<WORKER-SUBDOMAIN>.<DEFAULT-ACCOUNT-HOSTNAME>.dev/hello-worldにアクセスして、KVストアの値から応答を生成するデプロイされたWorkerを確認できます。

関連リソース