Skip to content

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

fetch で変換する

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

Workers では、カスタム URL スキームで画像を最適化できます。

Worker 内の fetch() サブリクエストでは、cf.image プロパティで画像最適化のパラメーターを設定できます。これは URL インターフェイス と同じ仕組みですが、画像リクエストごとにプログラムで制御できます。

URL ではなく画像バイトを直接扱う場合は、Images バインディング を使います。

Workers でできることの例は次のとおりです。

  • カスタム URL スキームを使う。画像 URL にピクセル寸法を書く代わりに、thumbnaillarge などのプリセット名を使います。
  • 元画像の実際の場所を隠す。外部の S3 バケットや、サーバー上の非公開フォルダーに画像を置いても、その情報を URL に出しません。
  • コンテンツネゴシエーションを実装する。端末やネットワークの状態に応じて、画像サイズ、フォーマット、品質を動的に変えられます。

仕組み

リサイズ機能は、Worker 内の fetch() サブリクエストオプション から使います。fetch() 関数は、第 2 引数の {cf: {image: {…}}} オブジェクトでパラメーターを受け取ります。

Worker 内で fetch(request) により画像を取得していた箇所に、次の例のようにオプションを追加します。

fetch(imageURL, {
	cf: {
		image: {
			fit: "scale-down",
			width: 800,
			height: 600,
		},
	},
});

これらの型定義は、Workers TypeScript 定義ライブラリ でも利用できます。

cf.image は Worker をホストする任意のゾーンで使えます。*.workers.dev サブドメインも含みます。各変換は、その Worker を所有するアカウントに課金されます。

Worker を設定する

Cloudflare ダッシュボードの Workers セクションで、新しいスクリプトを作成します。Worker スクリプトのスコープは、/images/*/assets/* など、アセット配信専用のパスにします。リサイズできるのは対応している画像フォーマットだけです。CSS や HTML など、それ以外のリソースをリサイズしようとするとエラーになります。

Worker が処理するパスは、元画像(未リサイズ)のパスと分けてください。画像リサイズ用 Worker が自分自身を呼び出すと、リクエストループになります。たとえば、画像は example.com/originals/ に置き、リサイズは example.com/thumbnails/*/originals/ から取得します。元画像の保存先を Worker が処理する場合は、無限ループにならないようにする必要があります。

リクエストループを防ぐ

リサイズと最適化を行うには、Worker がオリジンサーバーから元の未リサイズ画像を取得できる必要があります。Worker が処理するパスと、サーバー上の画像パスが重なると、Worker が自分自身に画像を要求して無限ループになることがあります。

オリジンサーバーへ直接送るべきリクエストを判別する必要があります。Via ヘッダーに image-resizing という文字列がある場合、そのリクエストは別の Worker からのもので、オリジンサーバーへ向ける必要があります。

export default {
	async fetch(request) {
		// If this request is coming from image resizing worker,
		// avoid causing an infinite loop by resizing it again:
		if (/image-resizing/.test(request.headers.get("via"))) {
			return fetch(request);
		}

		// Now you can safely use image resizing here
	},
};

ダッシュボードではプレビューできません

Worker エディターのスクリプトプレビューは fetch() オプションを無視し、常に未リサイズの画像を取得します。画像変換の効果を確認するには、Worker スクリプトをデプロイし、エディターの外で使います。

ローカル開発

wrangler dev の実行時、cf.image による変換は、精度の低いモックでローカルに適用されます。

リサイズ、回転、フォーマット、背景色など、一部のオプションだけが対象です。未対応のオプションは無視されます。

エラー処理

画像がリサイズできない場合(画像が存在しない、リサイズパラメーターが無効など)、レスポンスはエラーを示す HTTP ステータス(例: 400404502)になります。

デフォルトではエラーはブラウザーに転送されますが、扱い方は自分で決められます。たとえば、未リサイズの元画像へブラウザーをリダイレクトできます。

const response = await fetch(imageURL, options);

if (response.ok || response.redirected) {
	// fetch() may respond with status 304
	return response;
} else {
	return Response.redirect(imageURL, 307);
}

サーバー上の元画像が非常に大きい場合は、失敗した画像を表示しない方がよいことがあります。帯域やメモリを使いすぎたり、ページレイアウトが崩れたりするほど大きな画像へのフォールバックは避けてください。

失敗した画像をプレースホルダー画像に置き換えることもできます。

const response = await fetch(imageURL, options);
if (response.ok || response.redirected) {
	return response;
} else {
	// Change to a URL on your server
	return fetch("https://img.example.com/blank-placeholder.png");
}

Worker の例

https://example.com/image-resizingWorker を設定 し、https://example.com/image-resizing?width=80&image=https://example.com/uploads/avatar1.jpg のような URL を処理する場合の例です。

/**
 * Fetch and log a request
 * @param {Request} request
 */
export default {
	async fetch(request) {
		// Parse request URL to get access to query string
		let url = new URL(request.url);

		// Cloudflare-specific options are in the cf object.
		let options = { cf: { image: {} } };

		// Copy parameters from query string to request options.
		// You can implement various different parameters here.
		if (url.searchParams.has("fit"))
			options.cf.image.fit = url.searchParams.get("fit");
		if (url.searchParams.has("width"))
			options.cf.image.width = parseInt(url.searchParams.get("width"), 10);
		if (url.searchParams.has("height"))
			options.cf.image.height = parseInt(url.searchParams.get("height"), 10);
		if (url.searchParams.has("quality"))
			options.cf.image.quality = parseInt(url.searchParams.get("quality"), 10);

		// Your Worker is responsible for automatic format negotiation. Check the Accept header.
		const accept = request.headers.get("Accept");
		if (/image\/avif/.test(accept)) {
			options.cf.image.format = "avif";
		} else if (/image\/webp/.test(accept)) {
			options.cf.image.format = "webp";
		}

		// Get URL of the original (full size) image to resize.
		// You could adjust the URL here, e.g., prefix it with a fixed address of your server,
		// so that user-visible URLs are shorter and cleaner.
		const imageURL = url.searchParams.get("image");
		if (!imageURL)
			return new Response('Missing "image" value', { status: 400 });

		try {
			// TODO: Customize validation logic
			const { hostname, pathname } = new URL(imageURL);

			// Optionally, only allow URLs with JPEG, PNG, GIF, or WebP file extensions
			// @see https://developers.cloudflare.com/images/url-format#supported-formats-and-limitations
			if (!/\.(jpe?g|png|gif|webp)$/i.test(pathname)) {
				return new Response("Disallowed file extension", { status: 400 });
			}

			// Demo: Only accept "example.com" images
			if (hostname !== "example.com") {
				return new Response('Must use "example.com" source images', {
					status: 403,
				});
			}
		} catch (err) {
			return new Response('Invalid "image" value', { status: 400 });
		}

		// Build a request that passes through request headers
		const imageRequest = new Request(imageURL, {
			headers: request.headers,
		});

		// Returning fetch() with resizing options will pass through response with the resized image.
		return fetch(imageRequest, options);
	},
};

画像リサイズを試すときは、先にスクリプトをデプロイしてください。ダッシュボードのオンラインエディターでは、リサイズは有効になりません。

cacheKey に関する注意

リサイズ済み画像は常にキャッシュされます。fetch サブリクエストのフルサイズ元画像 URL に対するキャッシュエントリの、追加バリアントとしてキャッシュされます。多数の Worker や多数の外部 URL を使っても問題ありません。リサイズ済み画像のキャッシュには影響せず、正しくキャッシュするために追加の作業は不要です。

複数の元 URL のキャッシュをまとめるために cacheKey の fetch オプションを使う場合は、cacheKey にリサイズオプションを含めないでください。キャッシュが細分化され、キャッシュ性能が低下します。cacheKey はフルサイズの元画像 URL だけを参照し、リサイズ済みバージョンは含めません。

役に立ちましたか?