В разработке на платформе 1С часто возникает ситуация, когда необходимо отобразить пользователю картинку, хранящуюся в базе данных (в реквизите типа ХранилищеЗначения). Сложность заключается в том, что прямое обращение к базе данных возможно только на сервере, а методы взаимодействия с пользователем, такие как ПоказатьОповещениеПользователя, доступны исключительно на клиенте. В данной статье мы подробно разберем, как преодолеть этот контекстный барьер и передать данные эффективно.
Представим ситуацию: у нас есть справочник, где в одном из реквизитов хранится логотип или иконка в формате ХранилищеЗначения. Нам нужно реализовать механизм, который извлекает эту картинку и показывает её в системном оповещении (всплывающем окне в нижнем углу экрана). Разберем по шагам, почему нельзя просто вернуть «содержимое» хранилища и какие инструменты платформы нам помогут.
Для начала проанализируем типы данных. Объект ХранилищеЗначения не существует на клиенте. Однако объект типа Картинка является сериализуемым. Это означает, что платформа может автоматически преобразовать его в поток данных при передаче с сервера на клиент. Тем не менее, для работы в веб-клиенте или при передаче больших изображений более предпочтительным является использование временного хранилища.
Этот метод считается наиболее профессиональным и стабильным. Вместо того чтобы передавать «тяжелый» объект картинки целиком, мы помещаем его в специальную область памяти сервера и передаем на клиент лишь короткую строковую ссылку (адрес).
Рассмотрим алгоритм реализации. На сервере мы создаем функцию, которая извлекает данные и помещает их в хранилище:
&НаСервере
Функция ПолучитьАдресКартинкиИзХранилища(СсылкаНаОбъект)
// Получаем объект из базы данных
ОбъектСКартинкой = СсылкаНаОбъект.ПолучитьОбъект();
// Извлекаем данные из ХранилищаЗначения
ДанныеКартинки = ОбъектСКартинкой.РеквизитХранилище.Получить();
// Проверяем, что в хранилище действительно картинка или двоичные данные
Если ТипЗнч(ДанныеКартинки) <> Тип("Картинка") Тогда
КартинкаОбъект = Новый Картинка(ДанныеКартинки);
Иначе
КартинкаОбъект = ДанныеКартинки;
КонецЕсли;
// Помещаем во временное хранилище.
// Вторым параметром рекомендуется передавать УникальныйИдентификатор формы,
// чтобы данные жили до закрытия этой формы.
АдресВХ = ПоместитьВоВременноеХранилище(КартинкаОбъект, ЭтаФорма.УникальныйИдентификатор);
Возврат АдресВХ;
КонецФункции
Теперь на стороне клиента мы можем легко вызвать эту функцию и использовать полученный адрес для вывода оповещения:
&НаКлиенте
Процедура ПоказатьСообщениеПользователю()
// Вызываем серверную функцию для получения ссылки
АдресКартинки = ПолучитьАдресКартинкиИзХранилища(ВыбСсылка);
// Используем адрес в стандартной процедуре оповещения
ПоказатьОповещениеПользователя(
"Новое сообщение",
"Событие успешно обработано",
"Текст пояснения",
АдресКартинки
);
КонецПроцедуры
Как было замечено в обсуждении, объект Картинка сериализуется. Это означает, что его можно вернуть напрямую из серверной функции. Этот способ проще в кодировании, но может создавать нагрузку на сеть, если картинок много или они имеют большой размер.
Проанализируем пример кода для этого случая:
&НаСервереБезКонтекста
Функция ПолучитьОбъектКартинка(СсылкаНаЗапись)
Хранилище = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаЗапись, "ДанныеФайла");
Возврат Хранилище.Получить(); // Возвращаем объект типа Картинка напрямую
КонецФункции
&НаКлиенте
Процедура ВыполнитьОповещение()
НужнаяКартинка = ПолучитьОбъектКартинка(Элементы.Список.ТекущаяСтрока);
// Платформа корректно примет объект типа Картинка в качестве параметра
ПоказатьОповещениеПользователя("Внимание!", , "Данные загружены", НужнаяКартинка);
КонецПроцедуры
Разберем важный нюанс, упомянутый в теме: автор хотел использовать регламентное задание для показа оповещений. Выясним причину, по которой это нельзя сделать напрямую. Регламентное задание выполняется в фоновом сеансе на сервере. У этого сеанса нет пользовательского интерфейса («морды»), поэтому вызвать ПоказатьОповещениеПользователя внутри кода регламентного задания невозможно — система выдаст ошибку.
Для решения этой задачи мы рекомендуем использовать следующую схему:
ПодключитьОбработчикОжидания периодически (например, раз в минуту) опрашивает этот регистр.АдресВременногоХранилища) и вызывает ПоказатьОповещениеПользователя.При работе с изображениями в управляемом интерфейсе стоит придерживаться нескольких правил, чтобы не замедлять работу системы:
ХранилищеЗначения перед помещением в ПоместитьВоВременноеХранилище. Пустое хранилище может вызвать ошибку.Адреса Временного Хранилища (АВХ) является единственным надежным способом гарантировать отображение картинки.БиблиотекуКартинок конфигурации, обращаясь к ним как БиблиотекаКартинок.ИмяКартинки. Это избавляет от лишних вызовов сервера.РаботаСФайлами.СсылкаНаФайл().Таким образом, мы выяснили, что наиболее гибким и масштабируемым решением является использование временного хранилища. Это позволяет минимизировать объем передаваемых данных между сервером и клиентом и обеспечивает совместимость со всеми типами клиентских приложений 1С.