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