Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

静的アセットの保存と取得

Workers KV に静的アセットを保存し、Worker アプリケーションから低レイテンシかつ高スループットで配信します

最終更新 Markdown で表示Agent セットアップ

Workers KV に静的アセットを保存すると、これらのアセットをグローバルに、低レイテンシかつ高スループットで取得できます。アセットをそのまま配信することも、動的なレスポンスの生成に使うこともできます。カスタムスクリプト、KV の制限 に収まる小さな画像の配信や、翻訳などの静的アセットから動的な HTML レスポンスを生成するときに役立ちます。

Wrangler で静的アセットを Workers KV に書き込む

静的アセットを Workers KV に保存するには、Wrangler CLI(開発時によく使います)、Workers アプリケーションからの Workers KV バインディング、または Workers KV REST API(外部アプリケーションから Workers KV にアクセスするときに使います)を使えます。ここでは Wrangler CLI の使い方を示します。

このシナリオでは、サンプルの HTML ファイルを Workers KV ストアに保存します。

次の内容で新しいファイル index.html を作成します。

index.htmlhtml
Hello World!

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

npx wrangler kv key put index.html --path index.html --namespace-id=<ENTER_NAMESPACE_ID_HERE>

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

Worker アプリケーションから KV の静的アセットを配信する

この例では、Workers アプリケーションは HTTP リクエストのパスを任意のキー名として受け取り、そのキーに対応する KV ストアの値を返します。


import mime from "mime";

interface Env {
assets: KVNamespace;
}

export default {
	async fetch(request, env, ctx): Promise<Response> {
		// Return error if not a get request
		if(request.method !== 'GET'){
			return new Response('Method Not Allowed', {
				status: 405,
			})
		}

    	// Get the key from the url & return error if key missing
    	const parsedUrl = new URL(request.url)
    	const key = parsedUrl.pathname.replace(/^\/+/, '') // Strip any preceding /'s
    	if(!key){
    		return new Response('Missing path in URL', {
    			status: 400
    		})
    	}

    	// Get the mimetype from the key path
    	const extension = key.split('.').pop();
    	let mimeType = mime.getType(extension) || "text/plain";
    	if (mimeType.startsWith("text") || mimeType === "application/javascript") {
    		mimeType += "; charset=utf-8";
    	}

    	// Get the value from the Workers KV store and return it if found
    	const value = await env.assets.get(key, 'arrayBuffer')
    	if(!value){
    		return new Response("Not found", {
    			status: 404
    		})
    	}

    	// Return the response from the Workers application with the value from the KV store
    	return new Response(value, {
    		status: 200,
    		headers: new Headers({
    			"Content-Type": mimeType
    		})
    	});
    },

} satisfies ExportedHandler<Env>;
{
	"$schema": "node_modules/wrangler/config-schema.json",
	"name": "<ENTER_WORKER_NAME>",
	"main": "src/index.ts",
	"compatibility_date": "2025-03-03",
	"observability": {
		"enabled": true
	},
	"kv_namespaces": [
		{
			"binding": "assets",
			"id": "<YOUR_BINDING_ID>"
		}
	]
}

このコードは、HTTP リクエストから取得するキーと値のペアのキー名を解析します。次に、ブラウザーがレスポンスをどう扱うかを知らせるため、適切な MIME タイプを判定します。 KV ストアから値を取得するとき、このコードは arrayBuffer を使い、画像、ドキュメント、動画 / 音声ファイルなどのバイナリデータを正しく扱います。

Workers KV 名前空間に、キー index.html と HTML コンテンツを値とするサンプルのキーと値のペアがある場合、Workers アプリケーションの https://<YOUR-WORKER-HOSTNAME>/index.html にアクセスすると、index.html ファイルの内容を確認できます。

画像やドキュメントでも試すと、この Worker が KV からそれらのアセットも正しく配信することがわかります。

キーと値のペアから動的なレスポンスを生成する

静的アセットの配信に加え、KV ストアに保存した値をもとに、動的な HTML や API レスポンスを生成することもできます。

  1. まず、プロジェクトのルートに次のファイルを作成します。
hello-world.jsonjson
[
	{
		"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": "مرحبا بالعالم!"
	}
]
  1. ターミナルを開き、次の KV コマンドを入力して、翻訳ファイルの KV エントリを作成します。
npx wrangler kv key put hello-world.json --path hello-world.json --namespace-id=<ENTER_NAMESPACE_ID_HERE>
  1. Workers のコードを更新し、リクエストの Accept-Language ヘッダーの言語に基づいて、翻訳済み HTML ファイルを配信するロジックを追加します。
import mime from 'mime';
import parser from 'accept-language-parser'

interface Env {
assets: KVNamespace;
}

export default {
  async fetch(request, env, ctx): Promise<Response> {
    // Return error if not a get request
    if(request.method !== 'GET'){
      return new Response('Method Not Allowed', {
        status: 405,
      })
    }

    // Get the key from the url & return error if key missing
    const parsedUrl = new URL(request.url)
    const key = parsedUrl.pathname.replace(/^\/+/, '') // Strip any preceding /'s
    if(!key){
      return new Response('Missing path in URL', {
        status: 400
      })
    }

    	// Add handler for translation path (with early return)
    	if(key === 'hello-world'){
    		// Retrieve the language header from the request and the translations from Workers KV
    		const languageHeader = request.headers.get('Accept-Language') || 'en' // Default to English
    		const translations : {
    			"language_code": string,
    			"message": string
    		}[] = await env.assets.get('hello-world.json', 'json') || [];

    		// Extract the requested language
    		const supportedLanguageCodes = translations.map(item => item.language_code)
    		const languageCode = parser.pick(supportedLanguageCodes, languageHeader, {
    			loose: true
    		})

    		// Get the message for the selected language
    		let selectedTranslation = translations.find(item => item.language_code === languageCode)
    		if(!selectedTranslation) selectedTranslation = translations.find(item => item.language_code === "en")
    		const helloWorldTranslated = selectedTranslation!['message'];

    		// Generate and return the translated html
    		const html = `<!DOCTYPE html>
    		<html>
    			<head>
    				<title>Hello World translation</title>
    			</head>
    			<body>
    				<h1>${helloWorldTranslated}</h1>
    			</body>
    		</html>
    		`
    		return new Response(html, {
    			status: 200,
    			headers: {
    				'Content-Type': 'text/html; charset=utf-8'
    			}
    		})
    	}

    // Get the mimetype from the key path
    const extension = key.split('.').pop();
    let mimeType = mime.getType(extension) || "text/plain";
    if (mimeType.startsWith("text") || mimeType === "application/javascript") {
      mimeType += "; charset=utf-8";
    }

    // Get the value from the Workers KV store and return it if found
    const value = await env.assets.get(key, 'arrayBuffer')
    if(!value){
      return new Response("Not found", {
        status: 404
      })
    }

    // Return the response from the Workers application with the value from the KV store
    return new Response(value, {
      status: 200,
      headers: new Headers({
        "Content-Type": mimeType
      })
    });

},
} satisfies ExportedHandler<Env>;
{
	"$schema": "node_modules/wrangler/config-schema.json",
	"name": "<ENTER_WORKER_NAME>",
	"main": "src/index.ts",
	"compatibility_date": "2025-03-03",
	"observability": {
		"enabled": true
	},
	"kv_namespaces": [
		{
			"binding": "assets",
			"id": "<YOUR_BINDING_ID>"
		}
	]
}

この新しいコードは、翻訳済みレスポンスを返す専用エンドポイント /hello-world を提供します。この URL にアクセスすると、Worker のコードはまず、クライアントが Accept-Language リクエストヘッダーで要求した言語と、hello-world.json キーの翻訳を KV ストアから取得します。次に、翻訳済みメッセージを取得し、生成した HTML を返します。

Worker アプリケーションの https://<YOUR-WORKER-HOSTNAME>/hello-world にアクセスすると、適切に翻訳された "Hello World" メッセージが返ることがわかります。

ブラウザーの開発者コンソールで、ロケール言語を変更します(Chromium 系ブラウザーでは、Show Sensors を実行するとロケールのドロップダウンが表示されます)。Worker がロケール言語に応じた翻訳メッセージを返すことが確認できます。

関連リソース

役に立ちましたか?