← Images / images / optimization / transformations
Трансформация через fetch
Workers позволяет оптимизировать изображения по собственной схеме URL.
Параметры оптимизации изображений можно задать на уровне fetch() подзапрос в Worker с помощью cf.image В основе лежит тот же механизм, что и у Интерфейс URL, но даёт программный контроль над каждым запросом изображения.
Чтобы работать напрямую с байтами изображения вместо URL-адресов, используйте Привязка Images.
Вот несколько примеров гибкости, которую даёт Workers:
- Использование пользовательской схемы URL. Вместо указания размеров в пикселях в URL изображения используйте предустановленные имена, например
thumbnailиlarge. - Скрытие фактического расположения исходного изображения. Вы можете хранить изображения во внешнем bucket S3 или в скрытой папке на вашем сервере, не раскрывая эту информацию в URL.
- Реализация согласования содержимого. Это удобно для динамической адаптации размера, формата и качества изображения под устройство и состояние сети.
Как это работает
Функция изменения размера доступна через 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 исходного изображения в полном размере, а не на его изменённые версии.