Настройка автоматического наложения водяных знаков 1С-Битрикс
Менеджер загружает новое изображение товара через административную часть — и оно должно автоматически получить водяной знак без каких-либо дополнительных действий. Ручная обработка после загрузки — это сломанный процесс: кто-то забудет, кто-то сделает не так. Нужен перехват на уровне события загрузки файла.
Точки перехвата при загрузке изображений
В Битрикс файлы загружаются через CFile::SaveFile() и CFile::Add(). Событие OnBeforeFileAdd позволяет перехватить загрузку до сохранения на диск:
AddEventHandler('main', 'OnBeforeFileAdd', function(&$fileFields, $moduleId) {
// $moduleId: 'iblock', 'sale', 'catalog' и т.д.
if ($moduleId !== 'iblock') {
return; // Обрабатываем только изображения каталога
}
$mimeType = $fileFields['type'] ?? '';
if (!str_starts_with($mimeType, 'image/')) {
return;
}
$tmpFile = $fileFields['tmp_name'];
applyWatermarkInPlace($tmpFile);
});
Функция applyWatermarkInPlace модифицирует временный файл до его сохранения в /upload/. Это означает: в базе и на диске окажется уже обработанный файл, без необходимости хранить оригинал и кеш отдельно.
Различие между изменением и новой загрузкой
Событие OnBeforeFileAdd срабатывает только при новых загрузках. При обновлении элемента каталога, если изображение не менялось, файл не перезаписывается — событие не сработает. Это корректное поведение.
Проблема: если администратор загружает изображение через массовый импорт CSV или через REST API (iblock.element.update), событие OnBeforeFileAdd также должно срабатывать — и срабатывает, так как импорт в итоге вызывает CFile::SaveFile().
Исключения из автоматического наложения
Не все изображения нужно маркировать. Логотип компании в шапке, иконки категорий, системные изображения — всё это проходит через CFile. Фильтрация по $moduleId решает только часть задачи.
Для более точного управления добавляем проверку контекста через сессионную переменную:
AddEventHandler('main', 'OnBeforeFileAdd', function(&$fileFields, $moduleId) {
if ($moduleId !== 'iblock') return;
if (!isset($_SESSION['WATERMARK_ENABLED'])) return;
$iblockId = $_SESSION['CURRENT_IBLOCK_ID'] ?? null;
$noWatermarkIblocks = \Bitrix\Main\Config\Option::get('catalog', 'no_watermark_iblocks', '');
$excluded = array_map('intval', explode(',', $noWatermarkIblocks));
if ($iblockId && in_array($iblockId, $excluded)) return;
applyWatermarkInPlace($fileFields['tmp_name']);
});
Переменная CURRENT_IBLOCK_ID устанавливается в обработчике события OnBeforeIBlockElementAdd / OnBeforeIBlockElementUpdate. Список исключённых инфоблоков хранится в b_option.
Обработка ошибок при наложении
Если GD-обработка завершится с ошибкой (битый файл, неподдерживаемый формат), загрузка не должна блокироваться. Обёртка applyWatermarkInPlace в блоке try/catch — при ошибке файл сохраняется без водяного знака, ошибка пишется в лог:
function applyWatermarkInPlace(string $tmpFile): void
{
try {
// Обработка через GD
$result = processWatermark($tmpFile);
if ($result) {
file_put_contents($tmpFile, $result);
}
} catch (\Throwable $e) {
\Bitrix\Main\Diag\Debug::writeToFile(
$e->getMessage(), 'watermark_error', '/bitrix/watermark.log'
);
// Не прерываем загрузку — файл сохранится оригинальным
}
}
Производительность при массовой загрузке
При импорте каталога 5 000 товаров с 3 изображениями каждый — 15 000 операций GD. На среднем сервере обработка одного изображения занимает 50–150 мс. Итого: 12–37 минут добавляется ко времени импорта. Если это неприемлемо — переключаемся на асинхронную схему: сохраняем оригинал, ставим задачу в очередь b_agent, агент обрабатывает батчами по 100 изображений.
Что настраиваем
- Обработчик события
OnBeforeFileAddс фильтрацией по$moduleId - Список инфоблоков-исключений в
b_option - Обработку ошибок GD без блокировки загрузки
- Для массового импорта: асинхронную обработку через агент
- Мониторинг лога
/bitrix/watermark.logна предмет битых изображений







