Налаштування автоматичної оптимізації медіафайлів 1С-Бітрікс
Сторінка каталогу завантажує 40 зображень товарів загальною вагою 8 МБ. Google PageSpeed показує «Використовуйте зображення в сучасних форматах» та «Правильно задайте розмір зображень». Бітрікс зберігає оригінали в /upload/ та створює ресайзи через CFile::ResizeImageGet() — але формат залишається JPEG/PNG, і немає автоматичної конвертації в WebP. Налаштування автоматичної оптимізації — це кілька рівнів: ресайз під реальні розміри, конвертація в WebP, стиснення без втрат та віддача через CDN.
Рівень 1: ресайз при збереженні оригіналу
Стандартний CFile::ResizeImageGet() створює ресайз при першому запиті та кешує результат у /upload/resize_cache/. Основна проблема — оригінали зберігаються без стиснення, і менеджери завантажують фотографії з телефону розміром 5–10 МБ.
Стиснення при завантаженні через обробник події:
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'main',
'OnAfterFileSave',
function (\Bitrix\Main\Event $event) {
$file = $event->getParameter('FILE');
// Обробляємо тільки зображення
if (!in_array($file['CONTENT_TYPE'], ['image/jpeg', 'image/png', 'image/gif'])) {
return;
}
$path = $_SERVER['DOCUMENT_ROOT'] . $file['SRC'];
if (!file_exists($path)) {
return;
}
// Стиснення через Imagick або GD
\Local\Media\ImageOptimizer::compress($path, $file['CONTENT_TYPE']);
}
);
// /local/lib/Media/ImageOptimizer.php
namespace Local\Media;
class ImageOptimizer
{
public static function compress(string $path, string $contentType): void
{
if (!extension_loaded('imagick')) {
self::compressWithGd($path, $contentType);
return;
}
$img = new \Imagick($path);
// Видаляємо зайві метадані (EXIF, ICC)
$img->stripImage();
// Обмежуємо максимальний розмір оригіналу
$width = $img->getImageWidth();
$height = $img->getImageHeight();
if ($width > 2000 || $height > 2000) {
$img->resizeImage(2000, 2000, \Imagick::FILTER_LANCZOS, 1, true);
}
if ($contentType === 'image/jpeg') {
$img->setImageCompression(\Imagick::COMPRESSION_JPEG);
$img->setImageCompressionQuality(85);
} elseif ($contentType === 'image/png') {
$img->setImageCompression(\Imagick::COMPRESSION_ZIP);
$img->setOption('png:compression-level', '9');
}
$img->writeImage($path);
$img->destroy();
}
private static function compressWithGd(string $path, string $contentType): void
{
if ($contentType === 'image/jpeg') {
$img = imagecreatefromjpeg($path);
imagejpeg($img, $path, 85);
imagedestroy($img);
} elseif ($contentType === 'image/png') {
$img = imagecreatefrompng($path);
imagepng($img, $path, 9);
imagedestroy($img);
}
}
}
Рівень 2: конвертація в WebP
WebP дає 25–35% виграш у розмірі порівняно з JPEG при співставній якості. Підтримка браузерами — 97%+ (всі сучасні). Стратегія: генеруємо WebP-версію поряд з оригіналом, nginx віддає WebP браузерам, що підтримують формат.
Генерація WebP при ресайзі:
// Перевизначаємо логіку ресайзу в /local/php_interface/init.php
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'main',
'OnAfterGetResizeImagePath',
function (\Bitrix\Main\Event $event) {
$result = $event->getParameter('RESULT');
$src = $result['src'] ?? '';
if (!$src || !preg_match('/\.(jpg|jpeg|png)$/i', $src)) {
return;
}
$localPath = $_SERVER['DOCUMENT_ROOT'] . $src;
$webpPath = preg_replace('/\.(jpg|jpeg|png)$/i', '.webp', $localPath);
if (!file_exists($webpPath) && file_exists($localPath)) {
\Local\Media\WebpConverter::convert($localPath, $webpPath);
}
}
);
class WebpConverter
{
public static function convert(string $sourcePath, string $destPath): bool
{
if (extension_loaded('imagick')) {
$img = new \Imagick($sourcePath);
$img->setImageFormat('webp');
$img->setOption('webp:method', '6');
$img->setImageCompressionQuality(82);
$img->stripImage();
$img->writeImage($destPath);
$img->destroy();
return true;
}
if (function_exists('imagewebp')) {
$ext = strtolower(pathinfo($sourcePath, PATHINFO_EXTENSION));
$img = match($ext) {
'jpg', 'jpeg' => imagecreatefromjpeg($sourcePath),
'png' => imagecreatefrompng($sourcePath),
default => null,
};
if ($img) {
imagewebp($img, $destPath, 82);
imagedestroy($img);
return true;
}
}
return false;
}
}
Nginx: віддача WebP браузерам з підтримкою
map $http_accept $webp_suffix {
default "";
"~*webp" ".webp";
}
server {
location ~* ^(/upload/resize_cache/.+)\.(jpg|jpeg|png)$ {
set $img_path $1.$2;
set $webp_path $1.webp;
# Віддаємо WebP, якщо підтримується і файл існує
if ($webp_suffix = ".webp") {
add_header Vary Accept;
try_files $webp_path $img_path =404;
}
try_files $img_path =404;
expires 30d;
add_header Cache-Control "public, immutable";
}
}
Рівень 3: lazy loading та responsive images
В шаблоні компонента каталогу додаємо loading="lazy" та srcset:
// В шаблоні компонента: template.php
$img = \CFile::ResizeImageGet($element['PREVIEW_PICTURE'], ['width' => 300, 'height' => 300]);
$img2 = \CFile::ResizeImageGet($element['PREVIEW_PICTURE'], ['width' => 600, 'height' => 600]);
?>
<img
src="<?= htmlspecialchars($img['src']) ?>"
srcset="<?= htmlspecialchars($img['src']) ?> 300w, <?= htmlspecialchars($img2['src']) ?> 600w"
sizes="(max-width: 768px) 300px, 600px"
loading="lazy"
width="300"
height="300"
alt="<?= htmlspecialchars($element['NAME']) ?>"
>
Масова оптимізація існуючих зображень
Оптимізатор вже завантажених файлів запускається як агент Бітрікс або через CLI:
// Агент: обробляє по 100 файлів за запуск
$files = \Bitrix\Main\FileTable::getList([
'filter' => ['CONTENT_TYPE' => ['image/jpeg', 'image/png']],
'limit' => 100,
'offset' => (int)\Bitrix\Main\Config\Option::get('local.media', 'optimize_offset', 0),
])->fetchAll();
foreach ($files as $file) {
$path = \Bitrix\Main\IO\Path::combine(
\Bitrix\Main\Application::getDocumentRoot(),
$file['SUBDIR'], $file['FILE_NAME']
);
if (file_exists($path)) {
\Local\Media\ImageOptimizer::compress($path, $file['CONTENT_TYPE']);
}
}
\Bitrix\Main\Config\Option::set('local.media', 'optimize_offset',
(int)\Bitrix\Main\Config\Option::get('local.media', 'optimize_offset', 0) + 100
);
Терміни налаштування
Стиснення при завантаженні + WebP-конвертація в nginx + lazy loading в шаблонах — 1–2 робочих дні. Плюс масова оптимізація існуючого upload/ — додатково 4–8 годин на розробку агента та запуск.







