Как вывести изображения номенклатуры в отчете СКД и исправить ошибку «Несоответствие типов»?

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

Вывод изображений товаров в прайс-листах или каталогах — классическая задача при разработке отчетов в среде 1С. Однако при попытке адаптировать стандартные учебные примеры под реальные конфигурации (такие как 1С:Розница, 1С:Управление торговлей или 1С:ERP) разработчики часто сталкиваются с ошибкой «Несоответствие типов». Давайте подробно разберем, почему возникает эта ошибка, как работает механизм хранения картинок в Библиотеке Стандартных Подсистем (БСП) и рассмотрим три надежных способа решения этой задачи.

Выясняем причину ошибки «Несоответствие типов»

В типовых конфигурациях файлы и изображения практически никогда не хранятся напрямую в реквизитах справочника номенклатуры. Для этого используется механизм присоединенных файлов БСП. Ссылка на картинку ведет на элемент справочника НоменклатураПрисоединенныеФайлы, а сами данные могут физически находиться либо в специальном регистре сведений ПрисоединенныеФайлы (в СУБД), либо на внешнем диске (в томах хранения файлов).

Когда мы пытаемся получить изображение программно, ошибка «Несоответствие типов» чаще всего возникает на этапе создания объекта Картинка по следующим причинам:

  1. Отсутствие данных (Неопределено): Метод Получить() у хранилища значений возвращает значение Неопределено, если файл поврежден или физически отсутствует. Попытка передать такое значение в конструктор Новый Картинка() приводит к сбою программы.
  2. Неверный тип данных в базе: Иногда в базу данных записываются не «сырые» двоичные данные файла, а уже готовый сериализованный объект типа Картинка. В этом случае повторное оборачивание объекта в конструктор выдаст ошибку.
  3. Некорректный путь к файлу: При хранении файлов в томах на диске путь к файлу формируется динамически. Ошибки в косых чертах (слэшах) для систем на Linux или Windows, а также отсутствие прав у сервера 1С на чтение сетевого каталога вызывают сбой при инициализации картинки по сетевому пути.

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

Способ 1: Исправление программного обхода ячеек в ПриКомпоновкеРезультата

Если мы хотим пойти по пути ручного обхода ячеек результирующего табличного документа, нам нужно сделать этот процесс максимально безопасным. Разберем по шагам правильный алгоритм работы в процедуре ПриКомпоновкеРезультата модуля объекта отчета.

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


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    НастройкиКомпоновки = КомпоновщикНастроек.ПолучитьНастройки();
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, НастройкиКомпоновки, ДанныеРасшифровки);
    
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
    
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);
    
    КоличествоСтрок = ДокументРезультат.ВысотаТаблицы;
    КоличествоКолонок = ДокументРезультат.ШиринаТаблицы;
    
    Для СтрокаДокумента = 1 По КоличествоСтрок Цикл
        Для КолонкаДокумента = 1 По КоличествоКолонок Цикл
            
            Область = ДокументРезультат.Область(СтрокаДокумента, КолонкаДокумента);
            Расшифровка = Область.Расшифровка;
            
            Если ТипЗнч(Расшифровка) = Тип("ИдентификаторРасшифровкиКомпоновкиДанных") Тогда
                ПоляРасшифровки = ДанныеРасшифровки.Элементы.Получить(Расшифровка).ПолучитьПоля();
                НайденноеПоле = ПоляРасшифровки.Найти("Изображение");
                
                Если НайденноеПоле <> Неопределено Тогда
                    ХранимыйФайл = НайденноеПоле.Значение;
                    
                    // Проверяем заполненность ссылки и ее тип
                    Если ЗначениеЗаполнено(ХранимыйФайл) И Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(ХранимыйФайл)) Тогда
                        
                        УстановитьПривилегированныйРежим(Истина);
                        ДанныеИзображения = Неопределено;
                        
                        // Определяем, где хранится файл
                        Если ХранимыйФайл.ТипХраненияФайла = Перечисления.ТипыХраненияФайлов.ВИнформационнойБазе Тогда
                            СтуктураРег = РегистрыСведений.ПрисоединенныеФайлы.Получить(Новый Структура("ПрисоединенныйФайл", ХранимыйФайл));
                            ДанныеИзображения = СтуктураРег.ХранимыйФайл.Получить();
                        Иначе
                            // Собираем путь к файлу на диске
                            ПутьКФайлу = ?(ЗначениеЗаполнено(ХранимыйФайл.Том.ПолныйПутьWindows), ХранимыйФайл.Том.ПолныйПутьWindows, ХранимыйФайл.Том.ПолныйПутьLinux) + ХранимыйФайл.ПутьКФайлу;
                            ДанныеИзображения = ПутьКФайлу;
                        КонецЕсли;
                        
                        УстановитьПривилегированныйРежим(Ложь);
                        
                        // Если данные успешно получены, формируем картинку
                        Если ДанныеИзображения <> Неопределено Тогда
                            Попытка
                                ОбъектКартинка = Неопределено;
                                
                                // Проверяем, какой тип данных вернулся из хранилища
                                Если ТипЗнч(ДанныеИзображения) = Тип("Картинка") Тогда
                                    ОбъектКартинка = ДанныеИзображения;
                                ИначеЕсли ТипЗнч(ДанныеИзображения) = Тип("ДвоичныеДанные") Или ТипЗнч(ДанныеИзображения) = Тип("Строка") Тогда
                                    ОбъектКартинка = Новый Картинка(ДанныеИзображения);
                                КонецЕсли;
                                
                                Если ОбъектКартинка <> Неопределено Тогда
                                    // Отключаем автовысоту строки, чтобы избежать наложения картинок
                                    Область.АвтоВысотаСтроки = Ложь;
                                    Область.ВысотаСтроки = 45; // Задаем фиксированную высоту ячейки
                                    
                                    ИзображениеВДок = ДокументРезультат.Рисунки.Добавить(ТипРисункаТабличногоДокумента.Картинка);
                                    ИзображениеВДок.Картинка = ОбъектКартинка;
                                    ИзображениеВДок.РазмерКартинки = РазмерКартинки.Пропорционально;
                                    ИзображениеВДок.Расположить(Область);
                                КонецЕсли;
                            Исключение
                                // Записываем ошибку в журнал регистрации, чтобы отчет не падал у пользователя
                                ЗаписьЖурналаРегистрации("ВыводКартинкиСКД", УровеньЖурналаРегистрации.Предупреждение, , , ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
                            КонецПопытки;
                        КонецЕсли;
                        
                    КонецЕсли;
                КонецЕсли;
            КонецЕсли;
            
        КонецЦикла;
    КонецЦикла;
    
КонецПроцедуры

Способ 2: Использование стандартного программного интерфейса БСП

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

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

Рассмотрим, насколько проще и надежнее становится наш код при интеграции со стандартным API БСП:


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

Если ДвоичныеДанныеКартинки <> Неопределено Тогда
    Попытка
        ИзображениеВДок = ДокументРезультат.Рисунки.Добавить(ТипРисункаТабличногоДокумента.Картинка);
        ИзображениеВДок.Картинка = Новый Картинка(ДвоичныеДанныеКартинки);
        ИзображениеВДок.РазмерКартинки = РазмерКартинки.Пропорционально;
        ИзображениеВДок.Расположить(Область);
        
        Область.АвтоВысотаСтроки = Ложь;
        Область.ВысотаСтроки = 50; 
    Исключение
        // Обработка исключения при некорректном формате двоичных данных
    КонецПопытки;
КонецЕсли;

Этот метод гарантирует стабильную работу отчета независимо от того, перевели ли вы базы данных на Linux-сервер или изменили структуру каталогов на диске хранения томов.

Способ 3: Современный вывод картинок средствами СКД без программирования

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

Разберем по шагам, как настроить этот механизм непосредственно в схеме компоновки данных:

  1. Откроем Схему компоновки данных нашего отчета и перейдем на вкладку «Настройки».
  2. В структуре отчета выберем группировку или таблицу, где должна выводиться картинка.
  3. Перейдем на вкладку «Выбранные поля» и добавим туда поле, содержащее двоичные данные картинки (или навигационную ссылку на файл). Для конфигураций на БСП это может быть путь через точку: Номенклатура.ФайлПревью.ФайлХранилище.
  4. Перейдем на вкладку «Условное оформление» и нажмем кнопку «Добавить» для создания нового элемента оформления.
  5. В появившемся окне в параметре «Оформление» найдем свойство «Выводить картинку» (ВыводитьКартинку) и установим для него значение Да (или флаг активности).
  6. В этом же окне настроим параметр «Размер картинки», указав значение Пропорционально или Реальный размер в зависимости от требований к внешнему виду отчета.
  7. В параметре «Оформляемые поля» выберем именно то поле, которое содержит двоичные данные нашего изображения.
  8. В параметре «Условие» укажем, что оформление применяется только тогда, когда наше поле с изображением заполнено.

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

Оптимизация производительности: работа с большими файлами

Проанализируем еще один важный аспект. Пользователи часто загружают в базу данных фотографии товаров в исходном высоком разрешении (например, снимки с камер смартфонов объемом по 5–10 мегабайт). Если мы попытаемся программно вывести сотню таких картинок в один большой отчет (например, в прайс-лист), сервер 1С может аварийно завершить работу из-за нехватки оперативной памяти, либо формирование отчета займет недопустимо много времени.

Для предотвращения подобных ситуаций рекомендуется использовать объект платформы ОбрабатываемаяКартинка. Он позволяет налету сжимать и оптимизировать разрешение картинок перед выводом их в табличный документ:


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

Использование этого метода позволяет существенно сократить размер итогового табличного документа, ускорить его отправку на печать и сохранить стабильность работы сервера приложений 1С.

← На главную