INTEGRITY Документация

Трансформация через fetch

Workers позволяет оптимизировать изображения по собственной схеме URL.

Параметры оптимизации изображений можно задать на уровне fetch() подзапрос в Worker с помощью cf.image В основе лежит тот же механизм, что и у Интерфейс URL, но даёт программный контроль над каждым запросом изображения.

Чтобы работать напрямую с байтами изображения вместо URL-адресов, используйте Привязка Images.

Вот несколько примеров гибкости, которую даёт Workers:

Как это работает

Функция изменения размера доступна через options для fetch() подзапрос внутри Worker. fetch() функция принимает параметры во втором аргументе внутри {cf: {image: {…}}} объект.

В вашем Worker, где вы получаете изображение с помощью fetch(request), добавьте параметры, как в следующем примере:

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

Эти типы также доступны в наша библиотека определений TypeScript для Workers ↗.

cf.image доступен в любой зоне, где размещён Worker, включая *.workers.dev поддоменах. Каждое преобразование оплачивается со счёта аккаунта, которому принадлежит Worker.

Настройка Worker

Создайте новый скрипт в разделе Workers панели управления Cloudflare. Ограничьте область действия скрипта Worker путём, предназначенным для отдачи статических ресурсов, например /images/* или /assets/*. Изменять размер можно только у поддерживаемых форматов изображений. Попытка изменить размер ресурса другого типа (CSS, HTML) приведёт к ошибке.

Путь, который обрабатывает Worker, лучше отделить от пути к исходным (неизменённым) изображениям, чтобы избежать циклов запросов, когда Worker для изменения размера изображений вызывает сам себя. Например, храните изображения в example.com/originals/ каталог и обрабатывать изменение размера через example.com/thumbnails/* путь, который получает изображения из /originals/ каталог. Если исходные изображения хранятся в месте, которое обрабатывается Worker, необходимо не допустить создания бесконечного цикла в этом Worker.

Предотвращение циклов запросов

Для изменения размера и оптимизации Worker должен иметь возможность получить исходное, неизмененное изображение с вашего исходного сервера. Если путь, обрабатываемый вашим Worker, пересекается с путем хранения изображений на сервере, это может привести к бесконечному циклу, при котором Worker пытается запросить изображения сам у себя.

Необходимо определять, какие запросы должны отправляться напрямую на исходный сервер. Когда image-resizing строка присутствует в Via заголовок, это означает, что запрос поступает от другого 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 преобразования применяются локально с использованием упрощённой имитации низкой точности.

Поддерживается только часть параметров, включая resize, rotate, format и background colour. Неподдерживаемые параметры игнорируются.

Обработка ошибок

Если изменить размер изображения не удаётся (например, потому что изображение не существует или параметры изменения размера некорректны), ответ будет содержать HTTP статус с указанием ошибки (например, 400, 404, или 502).

По умолчанию ошибка передаётся в браузер, но вы можете сами определить, как её обрабатывать. Например, можно перенаправить браузер на исходное изображение без изменения размера:

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

Если вы настройка Worker на https://example.com/image-resizing для обработки URL вида https://example.com/image-resizing?width=80&image=https://example.com/uploads/avatar1.jpg:

/**
 * 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

Изображения с изменённым размером всегда кэшируются. Они кэшируются как дополнительные варианты в записи кэша для URL исходного полноразмерного изображения в fetch подзапрос. Не беспокойтесь о том, что используется много разных Worker или много внешних URL: это не влияет на кэширование изменённых изображений, и для их корректного кэширования ничего дополнительно делать не нужно.

Если вы используете cacheKey параметр fetch для объединения кешей нескольких исходных URL, не включайте параметры изменения размера в cacheKey. Это приведёт к фрагментации кэша и снизит производительность кэширования. cacheKey должен указывать только на URL исходного изображения в полном размере, а не на его изменённые версии.