Уменьшить размер картинки в 1С: сжатие и ресайз

Дата: 08.10.2026 17:48:14
Программирование 1С
Файлы 1С
БСП
Производительность
Обмен данными
Комментарий:
0
Платформа:
8.3
Конфигурация:
Любая
Формы:
Любые

Уменьшить размер картинки в 1С можно без сторонних программ: с платформы 8.3.14 есть объект ОбрабатываемаяКартинка, который меняет размер и формат картинки на сервере. Если нужен контроль качества JPEG или платформа ниже 8.3.14, подойдут WIA и GFLAx через COM в Windows, PowerShell-скрипт или ImageMagick на Linux. Ниже — готовые функции для каждого варианта и пакетное сжатие картинок номенклатуры с замером «было/стало».

Где встречается задача

Менеджеры годами прикрепляют к карточкам товаров фото с телефона как есть — по несколько мегабайт и тысяч пикселей на снимок, хотя сайту и печатной форме нужна картинка в разы меньше. Растут база и резервные копии, обмен с сайтом и выгрузка на маркетплейсы гоняют мегабайты и падают по таймауту.

В УТ 11 и КА 2 это присоединённые файлы справочника НоменклатураПрисоединенныеФайлы, основная картинка — реквизит ФайлКартинки номенклатуры (сверено по выгрузкам УТ 11.4 и КА 2.5). Байты лежат в томах на диске или в базе: в УТ 11.4 — в регистре сведений ДвоичныеДанныеФайлов, в КА 2.5 (более новая БСП) — в справочнике ХранилищеДвоичныхДанных. БСП пишет их со сжатием Новый СжатиеДанных(9), но JPEG и PNG уже сжаты — подробнее в статье про хранилище значения. Поэтому уменьшить вес картинок в базе 1С можно только одним путём: уменьшить сами картинки.

Чем сжать картинку в 1С: сравнение способов

СпособГде выполняетсяЧто установитьКачество JPEG
ОбрабатываемаяКартинкасервер, толстый клиент, внешнее соединениеничего, платформа 8.3.14+не задаётся
WIA.ImageFile + WIA.ImageProcessWindows: клиент или сервер 1Сничего, компонент Windowsсвойство Quality фильтра Convert
GFLAxWindows: клиент или сервер 1СGflAx, регистрация DLL 32 и 64 битпо свойствам объекта GflAx
PowerShell + System.DrawingWindowsскрипт .ps1параметр скрипта
ImageMagickLinux, есть сборки под Windowsпакет из репозиторияключ -quality
Облачный сервис с HTTP API (в заметке автора — optipic.io)любой сервер с выходом в интернетничего; обычно платно, картинки уходят третьей сторонев настройках сервиса

Как уменьшить размер картинки в 1С средствами платформы

Из объектов платформы изменить размер изображения в 1С программно умеет только ОбрабатываемаяКартинка. У объекта Картинка по синтакс-помощнику 8.3.27 есть Ширина(), Высота(), РазмерФайла() и КоличествоКадров() (8.3.14–8.3.15), но размер он не меняет. Преобразовать() с 8.3.14 не рекомендуется и, по примечанию в справке, переводит растровые картинки только в BMP и PNG — для фото это рост веса. Устаревший объект ОбработкаКартинок умеет лишь поворачивать и выделять значимую часть. ОбрабатываемаяКартинка доступна на сервере, в толстом клиенте и во внешнем соединении, поэтому функция живёт в серверном общем модуле.

// Общий модуль СжатиеКартинокСервер, флажок «Сервер».
// Платформа 8.3.15 и новее: ОбрабатываемаяКартинка и Картинка.КоличествоКадров().

// Уменьшает картинку так, чтобы длинная сторона была не больше МаксСторона.
// BMP и TIFF переводит в JPEG, PNG оставляет PNG: в нём бывает прозрачность.
// Возвращает структуру: Изменена (Булево), Данные (ДвоичныеДанные),
// Расширение (новое расширение без точки или "", если формат не менялся).
Функция УменьшитьКартинку(ДанныеКартинки, МаксСторона = 1200) Экспорт

    Результат = Новый Структура("Изменена, Данные, Расширение", Ложь, ДанныеКартинки, "");

    Исходная = Новый Картинка(ДанныеКартинки);
    ИсходныйФормат = Исходная.Формат();
    Ширина = Исходная.Ширина();
    Высота = Исходная.Высота();

    Растровые = Новый Массив;
    Растровые.Добавить(ФорматКартинки.JPEG);
    Растровые.Добавить(ФорматКартинки.PNG);
    Растровые.Добавить(ФорматКартинки.BMP);
    Растровые.Добавить(ФорматКартинки.TIFF);
    Если Растровые.Найти(ИсходныйФормат) = Неопределено
        Или Ширина = Неопределено Или Высота = Неопределено
        Или Исходная.КоличествоКадров() > 1 Тогда
        Возврат Результат; // вектор, GIF, иконки и многостраничный TIFF не трогаем
    КонецЕсли;

    НужноУменьшить = Макс(Ширина, Высота) > МаксСторона;
    НовыйФормат = ?(ИсходныйФормат = ФорматКартинки.PNG, ФорматКартинки.PNG, ФорматКартинки.JPEG);
    Если Не НужноУменьшить И НовыйФормат = ИсходныйФормат Тогда
        Возврат Результат; // размер подходит: JPEG лишний раз не пересжимаем
    КонецЕсли;

    Преобразование = Новый ОбрабатываемаяКартинка(Исходная);
    Если НужноУменьшить Тогда
        Коэффициент = МаксСторона / Макс(Ширина, Высота);
        Преобразование.УстановитьРазмер(Макс(1, Окр(Ширина * Коэффициент)), Макс(1, Окр(Высота * Коэффициент)));
    КонецЕсли;
    Преобразование.УстановитьФормат(НовыйФормат);
    НовыеДанные = Преобразование.ПолучитьКартинку().ПолучитьДвоичныеДанные();

    // Заменяем, только если файл стал легче хотя бы на 10%
    Если НовыеДанные = Неопределено Или НовыеДанные.Размер() > ДанныеКартинки.Размер() * 0.9 Тогда
        Возврат Результат;
    КонецЕсли;

    Результат.Изменена = Истина;
    Результат.Данные = НовыеДанные;
    Если НовыйФормат <> ИсходныйФормат Тогда
        Результат.Расширение = "jpg";
    КонецЕсли;
    Возврат Результат;

КонецФункции
  • Один коэффициент на обе стороны сохраняет пропорции; картинки меньше предела не растягиваются.
  • JPEG, который уже укладывается в предел, не пересохраняется: каждое пересохранение теряет качество, и повторный запуск портил бы те же фото.
  • BMP и TIFF уходят в JPEG — это самый большой выигрыш. PNG остаётся PNG: в нём бывает прозрачность.
  • Параметра качества JPEG у объекта нет; нужен контроль качества — берите WIA, PowerShell или ImageMagick. Результат принимается, только если файл стал легче хотя бы на 10%.

Пакетное сжатие файлов номенклатуры: было и стало

Сначала оцените масштаб — запрос можно выполнить в консоли запросов:

ВЫБРАТЬ
    Файлы.Расширение КАК Расширение,
    КОЛИЧЕСТВО(*) КАК Файлов,
    СУММА(Файлы.Размер) / 1048576 КАК ВсегоМБ,
    МАКСИМУМ(Файлы.Размер) / 1048576 КАК СамыйБольшойМБ
ИЗ
    Справочник.НоменклатураПрисоединенныеФайлы КАК Файлы
ГДЕ
    НЕ Файлы.ЭтоГруппа
    И НЕ Файлы.ПометкаУдаления
СГРУППИРОВАТЬ ПО
    Файлы.Расширение
УПОРЯДОЧИТЬ ПО
    ВсегоМБ УБЫВ

Пакетная функция ужимает тяжёлые картинки функцией УменьшитьКартинку и записывает их через программный интерфейс БСП РаботаСФайлами — одинаково для файлов в базе и в томах. Итог «было/стало» возвращается строкой и пишется в журнал регистрации; ошибка по одному файлу не останавливает остальные.

// Там же, в общем модуле СжатиеКартинокСервер.
// Пакетное сжатие картинок номенклатуры через БСП «Работа с файлами».
// Первый запуск — на копии базы: СжатьКартинкиНоменклатуры(1200, 300, 20).
// Возвращает итог «было/стало» и пишет его в журнал регистрации.
Функция СжатьКартинкиНоменклатуры(МаксСторона = 1200, ПорогКБ = 300, МаксимумФайлов = 0) Экспорт

    Расширения = Новый Массив;
    Для Каждого Расширение Из СтрРазделить("jpg,jpeg,png,bmp,tif,tiff", ",") Цикл
        Расширения.Добавить(Расширение);
        Расширения.Добавить(ВРег(Расширение));
    КонецЦикла;

    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    |    Файлы.Ссылка КАК Ссылка
    |ИЗ
    |    Справочник.НоменклатураПрисоединенныеФайлы КАК Файлы
    |ГДЕ
    |    НЕ Файлы.ЭтоГруппа
    |    И НЕ Файлы.ПометкаУдаления
    |    И НЕ Файлы.ХранитьВерсии
    |    И НЕ Файлы.Зашифрован
    |    И НЕ Файлы.ПодписанЭП
    |    И Файлы.Редактирует = ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)
    |    И Файлы.Размер > &Порог
    |    И Файлы.Расширение В (&Расширения)
    |УПОРЯДОЧИТЬ ПО
    |    Файлы.Размер УБЫВ";
    Если МаксимумФайлов > 0 Тогда
        Запрос.Текст = СтрЗаменить(Запрос.Текст, "ВЫБРАТЬ", "ВЫБРАТЬ ПЕРВЫЕ " + Формат(МаксимумФайлов, "ЧГ=0"));
    КонецЕсли;
    Запрос.УстановитьПараметр("Порог", ПорогКБ * 1024);
    Запрос.УстановитьПараметр("Расширения", Расширения);

    Было = 0;
    Стало = 0;
    Сжато = 0;
    БезИзменений = 0;
    Ошибок = 0;
    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл
        Попытка
            Замер = СжатьФайлКартинки(Выборка.Ссылка, МаксСторона);
            Если Замер = Неопределено Тогда
                БезИзменений = БезИзменений + 1;
            Иначе
                Было = Было + Замер.Было;
                Стало = Стало + Замер.Стало;
                Сжато = Сжато + 1;
            КонецЕсли;
        Исключение
            Ошибок = Ошибок + 1;
            ЗаписьЖурналаРегистрации("СжатиеКартинок", УровеньЖурналаРегистрации.Ошибка,
                Метаданные.Справочники.НоменклатураПрисоединенныеФайлы, Выборка.Ссылка,
                ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        КонецПопытки;
    КонецЦикла;

    Итог = СтрШаблон("Сжато файлов: %1, без изменений: %2, ошибок: %3. Было %4 МБ, стало %5 МБ.",
        Сжато, БезИзменений, Ошибок, Окр(Было / 1048576, 1), Окр(Стало / 1048576, 1));
    ЗаписьЖурналаРегистрации("СжатиеКартинок", УровеньЖурналаРегистрации.Информация, , , Итог);
    Возврат Итог;

КонецФункции

// Сжимает один присоединённый файл: Неопределено — файл не менялся,
// иначе структура Было, Стало (в байтах).
Функция СжатьФайлКартинки(ФайлСсылка, МаксСторона)

    ДанныеФайла = РаботаСФайлами.ДвоичныеДанныеФайла(ФайлСсылка, Ложь);
    Если ДанныеФайла = Неопределено Тогда
        Возврат Неопределено; // данных нет ни в базе, ни в томе
    КонецЕсли;

    Сжатие = УменьшитьКартинку(ДанныеФайла, МаксСторона);
    Если Не Сжатие.Изменена Тогда
        Возврат Неопределено;
    КонецЕсли;

    ИнформацияОФайле = Новый Структура;
    ИнформацияОФайле.Вставить("АдресФайлаВоВременномХранилище", ПоместитьВоВременноеХранилище(Сжатие.Данные));
    ИнформацияОФайле.Вставить("АдресВременногоХранилищаТекста", ""); // нужен даже пустой
    Если ЗначениеЗаполнено(Сжатие.Расширение) Тогда
        ИнформацияОФайле.Вставить("Расширение", Сжатие.Расширение);
    КонецЕсли;
    РаботаСФайлами.ОбновитьФайл(ФайлСсылка, ИнформацияОФайле);
    УдалитьИзВременногоХранилища(ИнформацияОФайле.АдресФайлаВоВременномХранилище);

    // ОбновитьФайл в БСП (УТ 11.4, КА 2.5) не пересчитывает реквизит Размер
    НовыйРазмер = Сжатие.Данные.Размер();
    ФайлОбъект = ФайлСсылка.ПолучитьОбъект();
    Если ФайлОбъект.Размер <> НовыйРазмер Тогда
        ФайлОбъект.Размер = НовыйРазмер;
        ФайлОбъект.Записать();
    КонецЕсли;
    РазблокироватьДанныеДляРедактирования(ФайлСсылка);

    Возврат Новый Структура("Было, Стало", ДанныеФайла.Размер(), НовыйРазмер);

КонецФункции
  • Отбор пропускает группы, помеченные на удаление, зашифрованные, подписанные и занятые файлы, а также файлы с версиями: ОбновитьФайл в БСП предназначен для файлов без версий.
  • ДвоичныеДанныеФайла(Ссылка, Ложь) вернёт Неопределено вместо исключения, если данных нет.
  • АдресВременногоХранилищаТекста нужен даже пустым: у справочника включён полнотекстовый поиск, и БСП читает это свойство без проверки.
  • В БСП из УТ 11.4 и КА 2.5 ОбновитьФайл не пересчитывает Размер ни у файлов в базе, ни у файлов в томах (видно по коду РаботаСФайламиСлужебный и РаботаСФайламиВТомахСлужебный). Функция дописывает его сама, иначе следующий запуск снова выберет те же файлы.

Перед запуском сделайте резервную копию базы (и каталога томов, если файлы там) и прогоните функцию на копии с МаксимумФайлов = 20: сжатие необратимо. Тысячи файлов удобнее обрабатывать в фоне — см. длительные операции БСП. Если справочник файлов входит в план обмена — например, в РИБ (план «Полный» в УТ 11.4 и КА 2.5), — изменённые картинки зарегистрируются к выгрузке, и первый обмен будет дольше обычного.

WIA.ImageFile в 1С: сжатие в Windows без установки

Windows Image Acquisition — COM-библиотека Windows (WIA.ImageFile, WIA.ImageProcess), ставить её не нужно. Приём из практики автора — фильтр Scale со свойствами MaximumWidth и MaximumHeight; фильтр Convert для формата и качества JPEG добавлен по документации Microsoft. Функция ниже серверная, для сервера 1С под Windows; для веб-клиента и Linux вариант не подходит. Внутри WIA обрабатывает картинки через GDI+, поэтому оговорки про службы Windows и Server Core из раздела о PowerShell относятся и к ней.

// Сжатие через WIA (Windows Image Acquisition): COM-объекты есть в Windows,
// ставить ничего не нужно. Тот же общий модуль, сервер 1С под Windows.
Функция УменьшитьЧерезWIA(ДанныеКартинки, МаксСторона = 1200, Качество = 80) Экспорт

    ИмяИсходного = ПолучитьИмяВременногоФайла("jpg");
    ИмяРезультата = ПолучитьИмяВременногоФайла("jpg"); // файла с этим именем ещё нет
    ДанныеКартинки.Записать(ИмяИсходного);

    ТекстОшибки = "";
    НовыеДанные = Неопределено;
    Попытка
        ПреобразоватьФайлWIA(ИмяИсходного, ИмяРезультата, МаксСторона, Качество);
        НовыеДанные = Новый ДвоичныеДанные(ИмяРезультата);
    Исключение
        ТекстОшибки = ОписаниеОшибки();
    КонецПопытки;
    УдалитьФайлТихо(ИмяИсходного);
    УдалитьФайлТихо(ИмяРезультата);

    Если ЗначениеЗаполнено(ТекстОшибки) Тогда
        ВызватьИсключение "WIA: " + ТекстОшибки;
    КонецЕсли;
    // Заменяем, только если файл стал легче хотя бы на 10%
    Возврат ?(НовыеДанные.Размер() < ДанныеКартинки.Размер() * 0.9, НовыеДанные, ДанныеКартинки);

КонецФункции

// COM-объекты живут только внутри процедуры: после выхода файлы свободны.
Процедура ПреобразоватьФайлWIA(ИмяИсходного, ИмяРезультата, МаксСторона, Качество)

    Изображение = Новый COMОбъект("WIA.ImageFile");
    Изображение.LoadFile(ИмяИсходного);
    Процессор = Новый COMОбъект("WIA.ImageProcess");

    Длинная = Макс(Изображение.Width, Изображение.Height);
    Если Длинная > МаксСторона Тогда
        // Scale вписывает картинку в рамку; рамку считаем сами с одним коэффициентом
        Коэффициент = МаксСторона / Длинная;
        Процессор.Filters.Add(Процессор.FilterInfos.Item("Scale").FilterID);
        Фильтр = Процессор.Filters.Item(Процессор.Filters.Count);
        Фильтр.Properties.Item("MaximumWidth").Value = Макс(1, Окр(Изображение.Width * Коэффициент));
        Фильтр.Properties.Item("MaximumHeight").Value = Макс(1, Окр(Изображение.Height * Коэффициент));
    КонецЕсли;

    // Convert — последним в цепочке: формат JPEG и качество
    Процессор.Filters.Add(Процессор.FilterInfos.Item("Convert").FilterID);
    Фильтр = Процессор.Filters.Item(Процессор.Filters.Count);
    Фильтр.Properties.Item("FormatID").Value = "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}"; // wiaFormatJPEG
    Фильтр.Properties.Item("Quality").Value = Качество;

    Результат = Процессор.Apply(Изображение);
    Результат.SaveFile(ИмяРезультата);

КонецПроцедуры

Процедура УдалитьФайлТихо(ИмяФайла)

    Попытка
        УдалитьФайлы(ИмяФайла);
    Исключение
        // не удалось — временный файл платформа удалит сама позже
    КонецПопытки;

КонецПроцедуры
  • Рамку для Scale функция считает сама, поэтому пропорции не зависят от настроек фильтра.
  • Convert ставится в цепочке последним — иначе WIA вернёт ошибку; Quality — от 1 до 100, по умолчанию 100. GUID — константа wiaFormatJPEG: при позднем связывании констант WIA в 1С нет.
  • COM-объекты живут в отдельной процедуре: после выхода из неё файлы свободны. PNG с прозрачностью сюда не отправляйте — результат всегда JPEG.

GFLAx в 1С: регистрация 32 и 64 бит, функция из практики

GFLAx — COM-обёртка GFL SDK от XnView, дистрибутив на сайте xnview.com. Её нужно зарегистрировать, и разрядность регистрации должна совпадать с процессом: 64-битный сервер 1С видит только 64-битную регистрацию, 32-битный клиент — только 32-битную. Порядок из практики автора для 64-битной Windows, пути полные:

rem cmd.exe от имени администратора. Архив GflAX-binaries-3.0.zip распакован в C:\Distr (путь условный)
rem 64-битная Windows: папка x64 - в System32, папка Win32 - в SysWOW64 (порядок из практики автора)
xcopy "C:\Distr\Unicode Release.x64" "C:\Windows\System32\Unicode Release.x64" /E /I
xcopy "C:\Distr\Unicode Release.Win32" "C:\Windows\SysWOW64\Unicode Release.Win32" /E /I

rem 64-битная регистрация - для 64-битного сервера 1С и 64-битного клиента
C:\Windows\System32\regsvr32.exe "C:\Windows\System32\Unicode Release.x64\GflAX.dll"
rem 32-битная - для 32-битного клиента 1С
C:\Windows\SysWOW64\regsvr32.exe "C:\Windows\SysWOW64\Unicode Release.Win32\GflAX.dll"

rem 32-битная Windows: только папка Win32 в System32 и regsvr32 оттуда же
rem Отменить регистрацию: тот же regsvr32 с ключом /u и тем же путём к DLL

Команды пишут в системные каталоги и реестр: до установки на рабочий сервер сделайте снимок или точку восстановления, ничего не заменяйте в System32 и проверьте лицензию GFL SDK. В заметке автора опечатка regsrv32 — правильно regsvr32. На 64-битной Windows System32\regsvr32.exe 64-битный, а SysWOW64\regsvr32.exe — 32-битный.

Функция автора ограничивала ширину 700 px и перезаписывала исходник. Ниже ограничена длинная сторона, маленькие картинки не растягиваются, результат пишется в новый файл. В первом варианте заметки была ошибка: внутри ?() стояли сравнения Делитель=Ширина/500, переменная получала тип Булево, и деление не работало.

// GFLAx из практики автора: длинная сторона не больше МаксСторона.
// Результат пишется в новый файл; исходник замените только после проверки.
Функция УменьшитьЧерезGFLAx(ИмяИсходного, ИмяРезультата, МаксСторона = 700) Экспорт

    GFLAx = Новый COMОбъект("GFLAx.GFLAx");
    GFLAx.LoadBitmap(ИмяИсходного);
    Ширина = GFLAx.Width;
    Высота = GFLAx.Height;

    Длинная = Макс(Ширина, Высота);
    Если Длинная <= МаксСторона Тогда
        Возврат Ложь; // уже меньше предела: не растягиваем и не пересохраняем
    КонецЕсли;

    // Один коэффициент на обе стороны — пропорции сохраняются
    Коэффициент = МаксСторона / Длинная;
    GFLAx.Resize(Макс(1, Окр(Ширина * Коэффициент)), Макс(1, Окр(Высота * Коэффициент)));
    GFLAx.SaveBitmap(ИмяРезультата);
    Возврат Истина;

КонецФункции

Формат и качество сохранения задаются свойствами GflAx — их имена сверьте по справке из архива вашей версии.

PowerShell и ImageMagick: внешняя программа на Windows и Linux

Третий путь — внешняя программа через ФайловаяСистема.ЗапуститьПрограмму из БСП (есть в УТ 11.4 и КА 2.5): она сама берёт в кавычки аргументы с пробелами, ждёт завершения и возвращает код возврата и поток ошибок. На Windows — PowerShell-скрипт с System.Drawing. Положите его на сервер 1С, например в C:\Scripts\resize-image.ps1, с правом записи только для администраторов: сервер выполнит всё, что там окажется.

# resize-image.ps1: уменьшает JPEG до MaxSide по длинной стороне и сохраняет с качеством Quality
param(
    [Parameter(Mandatory = $true)] [string] $InFile,
    [Parameter(Mandatory = $true)] [string] $OutFile,
    [int] $MaxSide = 1200,
    [int] $Quality = 80
)
$ErrorActionPreference = 'Stop'
Add-Type -AssemblyName System.Drawing

$src = [System.Drawing.Image]::FromFile($InFile)
try {
    # Фото с телефона: применяем поворот из EXIF (тег 0x0112), иначе картинка ляжет набок
    if ($src.PropertyIdList -contains 0x0112) {
        switch ($src.GetPropertyItem(0x0112).Value[0]) {
            3 { $src.RotateFlip([System.Drawing.RotateFlipType]::Rotate180FlipNone) }
            6 { $src.RotateFlip([System.Drawing.RotateFlipType]::Rotate90FlipNone) }
            8 { $src.RotateFlip([System.Drawing.RotateFlipType]::Rotate270FlipNone) }
        }
    }
    # Маленькие картинки не растягиваем: коэффициент не больше 1
    $k = [Math]::Min(1.0, $MaxSide / [Math]::Max($src.Width, $src.Height))
    $w = [Math]::Max(1, [int][Math]::Round($src.Width * $k))
    $h = [Math]::Max(1, [int][Math]::Round($src.Height * $k))

    $dst = New-Object System.Drawing.Bitmap($w, $h)
    try {
        $g = [System.Drawing.Graphics]::FromImage($dst)
        $g.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
        $g.Clear([System.Drawing.Color]::White)
        $g.DrawImage($src, 0, 0, $w, $h)
        $g.Dispose()

        $jpeg = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() |
            Where-Object { $_.MimeType -eq 'image/jpeg' }
        $params = New-Object System.Drawing.Imaging.EncoderParameters(1)
        $params.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter([System.Drawing.Imaging.Encoder]::Quality, [long]$Quality)
        $dst.Save($OutFile, $jpeg, $params)
    }
    finally {
        $dst.Dispose()
    }
}
finally {
    $src.Dispose()
}

Ключ -ExecutionPolicy Bypass действует только на этот процесс и в реестр не пишет, но политику из групповой политики не перекрывает — тогда скрипт придётся подписать или согласовать исключение с администратором. Кроме того, по документации Microsoft GDI+, на котором стоит System.Drawing, не поддерживается в службах Windows, а сервер 1С работает службой; в Server Core GDI+ в составе ОС нет. Поэтому PowerShell — для разовой чистки с проверкой на копии, а регламентное задание стройте на платформенном способе или ImageMagick.

// Внешняя программа через БСП, выполняется на сервере 1С:
// Windows — скрипт resize-image.ps1, Linux — ImageMagick. Только для JPEG.
// Процедура УдалитьФайлТихо — из листинга с WIA.
Функция УменьшитьВнешнейПрограммой(ДанныеКартинки, МаксСторона = 1200, Качество = 80) Экспорт

    Исходная = Новый Картинка(ДанныеКартинки);
    Если Исходная.Формат() <> ФорматКартинки.JPEG Тогда
        Возврат ДанныеКартинки; // PNG с прозрачностью и прочее — платформенным способом
    КонецЕсли;
    НужноУменьшить = Макс(Исходная.Ширина(), Исходная.Высота()) > МаксСторона;

    ИмяИсходного = ПолучитьИмяВременногоФайла("jpg");
    ИмяРезультата = ПолучитьИмяВременногоФайла("jpg");
    ДанныеКартинки.Записать(ИмяИсходного);
    // Числа в командную строку — через Формат: Строка(1200) даст "1 200"
    Предел = Формат(МаксСторона, "ЧГ=0");
    СтрокаКачества = Формат(Качество, "ЧГ=0");

    Команда = Новый Массив;
    Если ОбщегоНазначения.ЭтоWindowsСервер() Тогда
        Команда.Добавить("powershell.exe");
        Команда.Добавить("-NoProfile");
        Команда.Добавить("-NonInteractive");
        Команда.Добавить("-ExecutionPolicy");
        Команда.Добавить("Bypass");
        Команда.Добавить("-File");
        Команда.Добавить("C:\Scripts\resize-image.ps1");
        Команда.Добавить("-InFile");
        Команда.Добавить(ИмяИсходного);
        Команда.Добавить("-OutFile");
        Команда.Добавить(ИмяРезультата);
        Команда.Добавить("-MaxSide");
        Команда.Добавить(Предел);
        Команда.Добавить("-Quality");
        Команда.Добавить(СтрокаКачества);
    Иначе
        Команда.Добавить("convert"); // ImageMagick 6; в версии 7 — "magick"
        Команда.Добавить(ИмяИсходного);
        Команда.Добавить("-auto-orient"); // повернуть по EXIF до удаления метаданных
        Если НужноУменьшить Тогда
            Команда.Добавить("-resize");
            Команда.Добавить(Предел + "x" + Предел); // вписать в квадрат, пропорции сохраняются
        КонецЕсли;
        Команда.Добавить("-strip");
        Команда.Добавить("-quality");
        Команда.Добавить(СтрокаКачества);
        Команда.Добавить(ИмяРезультата);
    КонецЕсли;

    ПараметрыЗапуска = ФайловаяСистема.ПараметрыЗапускаПрограммы();
    ПараметрыЗапуска.ДождатьсяЗавершения = Истина;
    ПараметрыЗапуска.ПолучитьПотокОшибок = Истина;
    Результат = ФайловаяСистема.ЗапуститьПрограмму(Команда, ПараметрыЗапуска);

    НовыеДанные = Неопределено;
    ФайлРезультата = Новый Файл(ИмяРезультата);
    Если Результат.КодВозврата = 0 И ФайлРезультата.Существует() Тогда
        НовыеДанные = Новый ДвоичныеДанные(ИмяРезультата);
    КонецЕсли;
    УдалитьФайлТихо(ИмяИсходного);
    УдалитьФайлТихо(ИмяРезультата);

    Если НовыеДанные = Неопределено Тогда
        ВызватьИсключение СтрШаблон("Программа завершилась с кодом %1: %2",
            Результат.КодВозврата, Результат.ПотокОшибок);
    КонецЕсли;
    // Заменяем, только если файл стал легче хотя бы на 10%
    Возврат ?(НовыеДанные.Размер() < ДанныеКартинки.Размер() * 0.9, НовыеДанные, ДанныеКартинки);

КонецФункции
  • Числа передавайте через Формат(Число, "ЧГ=0"): Строка(1200) даст «1 200» с неразрывным пробелом, и программа не разберёт такое число.
  • -resize добавляется, только если картинка больше предела, иначе ImageMagick её растянет; -auto-orient поворачивает фото по EXIF до того, как -strip удалит метаданные.
  • Синтакс-помощник 8.3.27 предупреждает об исключении при вызове ЗапуститьПриложение на сервере, хотя БСП делает именно так, — проверьте функцию на своём релизе.

Сервер на Linux. COM там нет, поэтому WIA и GFLAx отпадают. Остаются ОбрабатываемаяКартинка (ограничений по ОС в синтакс-помощнике нет — проверьте на своём релизе) и ImageMagick из репозитория: команда convert в версии 6 и magick в версии 7. При включённых профилях безопасности кластера программу нужно добавить в разрешённые приложения профиля.

Типичные ошибки

  • «Тип не определен (ОбрабатываемаяКартинка)» — код попал в процедуру &НаКлиенте. В тонком клиенте этого типа нет: уменьшайте картинку на сервере.
  • «Недопустимая строка с указанием класса» при Новый COMОбъект("GFLAx.GFLAx") — библиотека не зарегистрирована или зарегистрирована не той разрядности.
  • «Поле объекта не обнаружено (АдресВременногоХранилищаТекста)» — в структуру для ОбновитьФайл не добавили адрес текста.
  • Картинка легла на бок: поворот фото хранится в EXIF, а инструмент его не применил. ImageMagick с -auto-orient и скрипт выше его учитывают; платформенный вариант и WIA проверьте на своих фото.
  • Маленькие картинки стали мыльными — их растянули. Сравнивайте длинную сторону с пределом до ресайза.
  • Качество падает с каждым запуском обработки — JPEG пересохраняется повторно. Не трогайте то, что уже меньше предела.
  • SaveFile в WIA поверх существующего файла писать не стоит: по практике метод его не перезаписывает, а завершается ошибкой. Функция выше берёт новое имя из ПолучитьИмяВременногоФайла() — по справке этот метод файл не создаёт.

Частые вопросы

Почему после сжатия база не стала меньше?

В файловой базе место внутри 1Cv8.1CD освобождается, но сам файл не уменьшается: нужна процедура «Тестирование и исправление» в конфигураторе с флажком «Сжатие таблиц информационной базы» — в монопольном режиме и после резервной копии. В клиент-серверной базе место освобождается внутри файлов СУБД; сжимать сами файлы MS SQL или PostgreSQL — отдельная плановая операция.

Можно ли уменьшать картинку сразу при загрузке?

Да, но на сервере: в тонком клиенте ОбрабатываемаяКартинка недоступна. Надёжнее всего регламентное задание раз в сутки с вызовом СжатьКартинкиНоменклатуры — новые тяжёлые файлы попадут под отбор сами. Подписка на запись файла хуже: ОбновитьФайл снова запишет файл, обработчик сработает повторно, а пользователь будет ждать сжатия.

Какой размер и качество выбрать?

Ориентируйтесь на самое крупное место показа: карточку товара на сайте и требования маркетплейса к фото. Разумная отправная точка — 1200–1600 px по длинной стороне и качество JPEG 75–85; проверьте на своих снимках. Уменьшенную картинку обратно не увеличить, поэтому предел берите с запасом.

Что делать с PNG?

Логотипы и картинки с прозрачностью оставляйте в PNG — УменьшитьКартинку их только уменьшит. Фото, сохранённые в PNG по ошибке, выгоднее перевести в JPEG отдельным отбором: прозрачность при этом пропадёт.

Когда проще заказать

Если картинок десятки тысяч, база распределённая или обмен с сайтом уже падает, проще поручить работу программисту: он подберёт способ под вашу платформу и ОС, настроит регламентное задание и проверит обмен после сжатия. Такая задача относится к доработке отчётов и обработок — разместите задачу, оценка бесплатная, ответ в течение рабочего дня.

По теме

Не хотите разбираться сами?

Адаптируем это решение под вашу базу. Оценка бесплатно.

Оставить задачу

Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.


1С, 1С:Предприятие — товарные знаки ООО «1С». Сайт не является официальным представительством фирмы «1С» и не аффилирован с ней.