Почему зависает формирование прайс-листа с картинками в 1С и как это исправить?

Программист 1С v8.3 (Управляемые формы) Торговля и дистрибуция
← На главную

При разработке отчетов и прайс-листов в 1С на базе системы компоновки данных (СКД) или через ручной вывод в ТабличныйДокумент часто возникает ситуация, когда добавление графических изображений приводит к критическому падению производительности. Отчет может формироваться часами или вызывать аварийное завершение работы клиентского приложения.

Проанализируем причины такого поведения платформы 1С:Предприятие, разберем типичные ошибки в программном коде и рассмотрим эффективные способы оптимизации формирования графических прайс-листов.

Основные причины зависания табличного документа с изображениями

Выясним причину, по которой даже небольшой каталог товаров может парализовать работу платформы при выводе изображений:

  1. Декодирование и потребление оперативной памяти: Сжатые файлы формата JPEG или PNG при помещении в ячейки отчета распаковываются графической подсистемой платформы в неупакованный растровый формат (BMP). Несколько десятков оригинальных фотографий с высоким разрешением (например, 10–20 Мпикс) в развернутом виде занимают гигабайты оперативной памяти. В 32-разрядном клиенте 1С это мгновенно приводит к переполнению адресного пространства процесса и зависанию из-за своппинга.
  2. Перерасчет слоя рисунков: Коллекция Рисунки табличного документа представляет собой оверлей над сеткой ячеек. При добавлении сотен графических объектов и вызове метода Расположить() платформа пересчитывает координаты и привязки каждого объекта. Вставка свойства Картинка в цикле порождает колоссальную нагрузку на графический интерфейс.
  3. Выполнение запросов в цикле: Получение двоичных данных отдельным запросом для каждой строки отчета многократно увеличивает накладные расходы на обращение к СУБД.
  4. Сериализация тяжелого документа: Если отчет формируется на стороне сервера, табличный документ целиком упаковывается и передается на клиент. Передача сотен мегабайт графических данных по сети вызывает долгое ожидание на этапе десериализации.

Способ 1. Использование уменьшенных копий (миниатюр / превью)

Разберем главное правило: для отображения в табличном документе ячейка имеет фиксированный размер (например, 150х100 точек), поэтому выгружать в нее полноразмерный исходный файл бессмысленно. Исходные файлы должны оставаться в хранилище, а в отчет необходимо передавать миниатюры.

Для генерации превью «на лету» без сторонних библиотек используем объект ОбрабатываемаяКартинка:


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

Практическая рекомендация: Чтобы не тратить процессорное время сервера на масштабирование при каждом формировании отчета, сохраняйте миниатюры заранее. Создайте дополнительный реквизит или регистр сведений, куда превью записывается однократно в момент загрузки основного изображения номенклатуры.

Способ 2. Устранение запросов в цикле и пакетная выборка данных

Рассмотрим исходный код постобработки отчета в процедуре ПриКомпоновкеРезультата. Вызов функции ПолучитьИзображение внутри цикла по строкам таблицы для каждой позиции порождает отдельный запрос к базе данных:


// Неоптимальный вариант: запрос внутри цикла
Для Ст = 7 По КоличествоСтрокТаблицы Цикл
    ТекОбласть = ДокументРезультат.Область(Ст, 9);
    // ...
    Рисунок = ПолучитьИзображение(СсылкаНаФайл); // Выполняет запрос к БД на каждой итерации!
    // ...
КонецЦикла;

Оптимизируем логику: выберем все необходимые изображения одним пакетным запросом заранее перед обходом строк табличного документа или добавим поле с двоичными данными непосредственно в набор данных СКД.

Посмотрим на пример с предварительной выборкой изображений в таблицу соответствия:


// Оптимальный вариант: предварительный сбор ссылок и пакетный запрос
МассивФайлов = Новый Массив;

// Первый проход: собираем только ссылки на файлы
Для Ст = 7 По ДокументРезультат.ВысотаТаблицы Цикл
    ТекОбласть = ДокументРезультат.Область(Ст, 9);
    Если ТекОбласть.Расшифровка <> Неопределено Тогда
        ЭлементРасшифровки = ДанныеРасшифровки.Элементы[ТекОбласть.Расшифровка];
        Если ТипЗнч(ЭлементРасшифровки) = Тип("ЭлементРасшифровкиКомпоновкиДанныхПолей") Тогда
            Поля = ЭлементРасшифровки.ПолучитьПоля();
            Для Каждого Поле Из Поля Цикл
                Если Поле.Поле = "ФайлСсылка" И ЗначениеЗаполнено(Поле.Значение) Тогда
                    МассивФайлов.Добавить(Поле.Значение);
                КонецЕсли;
            КонецЦикла;
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

// Получаем все миниатюры одним запросом к базе данных
СоответствиеКартинок = Новый Соответствие;
Если МассивФайлов.Количество() > 0 Тогда
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    ПрисоединенныеФайлы.ПрисоединенныйФайл КАК Ссылка,
        |    ПрисоединенныеФайлы.ХранимыйФайл КАК ХранимыйФайл
        |ИЗ
        |    РегистрСведений.ПрисоединенныеФайлы КАК ПрисоединенныеФайлы
        |ГДЕ
        |    ПрисоединенныеФайлы.ПрисоединенныйФайл В(&МассивФайлов)";
    
    Запрос.УстановитьПараметр("МассивФайлов", МассивФайлов);
    Выборка = Запрос.Выполнить().Выбрать();
    
    Пока Выборка.Следующий() Цикл
        ДвоичныеДанные = Выборка.ХранимыйФайл.Получить();
        Если ДвоичныеДанные <> Неопределено Тогда
            СоответствиеКартинок.Вставить(Выборка.Ссылка, Новый Картинка(ДвоичныеДанные, Истина));
        КонецЕсли;
    КонецЦикла;
КонецЕсли;

// Второй проход: быстрая расстановка картинок из памяти
Для Ст = 7 По ДокументРезультат.ВысотаТаблицы Цикл
    ТекОбласть = ДокументРезультат.Область(Ст, 9);
    Если ТекОбласть.Расшифровка <> Неопределено Тогда
        // Получаем ссылку на файл из расшифровки
        // ...
        КартинкаТовара = СоответствиеКартинок[ТекущаяСсылка];
        Если КартинкаТовара <> Неопределено Тогда
            Рис = ДокументРезультат.Рисунки.Добавить(ТипРисункаТабличногоДокумента.Картинка);
            Рис.РазмерКартинки = РазмерКартинки.Пропорционально;
            Рис.Картинка = КартинкаТовара;
            Рис.Расположить(ТекОбласть);
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

Способ 3. Вывод через встроенные механизмы макетов СКД

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

  1. В наборе данных СКД формируем вычисляемое поле или поле запроса, возвращающее значение с типом Картинка.
  2. На вкладке Макеты схемы компоновки добавляем Макет поля для поля с картинкой.
  3. В ячейку макета помещаем параметр, привязанный к полю картинки, и в свойствах ячейки указываем положение и режим масштабирования.

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

← На главную