При разработке интерфейсов в веб-клиенте программисты часто сталкиваются с проблемой: при попытке загрузить картинку в табличную часть объекта (например, справочника «Номенклатура»), данные внезапно «исчезают» (принимают значение Неопределено) сразу после выполнения преобразования ЗначениеВРеквизитФормы. Это происходит из-за особенностей работы управляемых форм и механизмов сериализации данных.
В этой статье мы подробно разберем, почему возникает эта ошибка, как правильно организовать хранение изображений и какой подход считается «золотым стандартом» в современной разработке на платформе 1С:Предприятие 8.3.
Основная причина, по которой после выполнения кода ЗначениеВРеквизитФормы(НоменклатураОбъект, "Объект") поле с картинкой очищается, кроется в несовместимости типов. Объект платформы ХранилищеЗначения предназначен исключительно для работы на стороне сервера и хранения данных непосредственно в базе (в реквизитах объектов). Однако реквизиты формы (структуры типа ДанныеФормыСтруктура или коллекции ДанныеФормыКоллекция) не поддерживают этот тип данных.
Когда вы выполняете преобразование «Объект -> Реквизит формы», платформа пытается сопоставить поля. Поскольку в реквизитах формы нет типа, эквивалентного ХранилищеЗначения, данные просто игнорируются или сбрасываются. В веб-клиенте эта ситуация усугубляется строгими правилами передачи данных между браузером и сервером.
Проанализируем наиболее эффективный способ работы в рамках формы. Вместо того чтобы пытаться сразу поместить двоичные данные в реквизит объекта, мы будем использовать адрес временного хранилища. Рассмотрим этот алгоритм по шагам.
Шаг 1. Подготовка формы. Добавим в табличную часть ВебКартинки на форме дополнительный реквизит АдресКартинки с типом Строка (длина неограничена). Этот реквизит не обязательно должен существовать в самом объекте в метаданных, он может быть чисто экранным (реквизитом данных формы).
Шаг 2. Загрузка файла. Используем асинхронные методы для помещения файла на сервер. Посмотрим на пример кода:
&НаКлиенте
Асинх Процедура ВыбратьКартинку(Команда)
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Фильтр = "Картинки (*.jpg;*.png)|*.jpg;*.png";
Результат = Ждать ПоместитьФайлыНаСерверАсинх(,,, Диалог, УникальныйИдентификатор);
Если Результат = Неопределено Тогда
Возврат;
КонецЕсли;
Для каждого Файл Из Результат Цикл
ДобавитьСтрокуСКартинкой(Файл.Адрес, Файл.Файл.Имя);
КонецЦикла;
КонецПроцедуры
Шаг 3. Обработка на сервере. Здесь мы не трогаем СправочникОбъект раньше времени. Мы просто добавляем данные в коллекцию формы:
&НаСервере
Процедура ДобавитьСтрокуСКартинкой(Адрес, ИмяФайла)
НоваяСтрока = Объект.ВебКартинки.Добавить();
НоваяСтрока.ИмяФайла = ИмяФайла;
// Сохраняем только временный адрес!
НоваяСтрока.АдресКартинки = Адрес;
КонецПроцедуры
Шаг 4. Отображение. Чтобы пользователь видел картинку в интерфейсе, в элементе формы «Поле картинки» (которое находится внутри таблицы или отдельно) в свойстве ПутьКДанным укажем Объект.ВебКартинки.ТекущиеДанные.АдресКартинки. Платформа 1С сама подтянет изображение из временного хранилища по этой ссылке.
Как справедливо отмечают эксперты, хранить бинарные данные (картинки) в табличной части основного справочника — это плохая практика. Если у вас будет 10 000 товаров, и у каждого по 3–5 тяжелых фото в ТЧ, справочник Номенклатура станет весить гигабайты. Это приведет к критическому замедлению работы при любом поиске или открытии списка.
Разберем канонический подход (используемый в БСП):
НоменклатураПрисоединенныеФайлы.Файл с типом ХранилищеЗначения.Номенклатуры храним только Ссылку на этот новый справочник.Это позволит основной таблице оставаться «легкой», а данные файлов будут подгружаться только тогда, когда они действительно нужны.
Выясним, как окончательно зафиксировать картинки в базе данных. Лучшее место для этого — системное событие ПередЗаписьюНаСервере. В этот момент у нас есть доступ и к данным формы (Объект), и к самому записываемому объекту базы данных (ТекущийОбъект).
Проанализируем пример реализации:
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
Для каждого СтрокаТЧ Из Объект.ВебКартинки Цикл
// Проверяем, является ли адрес ссылкой на временное хранилище
Если ЭтоАдресВременногоХранилища(СтрокаТЧ.АдресКартинки) Тогда
ДвоичныеДанные = ПолучитьИзВременногоХранилища(СтрокаТЧ.АдресКартинки);
// Находим соответствующую строку в записываемом объекте
// (используем индекс или идентификатор строки)
Индекс = Объект.ВебКартинки.Индекс(СтрокаТЧ);
СтрокаОбъекта = ТекущийОбъект.ВебКартинки[Индекс];
// Вот здесь мы работаем с ХранилищемЗначения в Объекте БД
СтрокаОбъекта.Хранилище = Новый ХранилищеЗначения(ДвоичныеДанные);
// Удаляем из временного хранилища, чтобы не забивать память
УдалитьИзВременногоХранилища(СтрокаТЧ.АдресКартинки);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Рассмотрим специфику, которую необходимо учитывать при работе через браузер:
1. Навигационные ссылки: Метод ПолучитьНавигационнуюСсылку(Объект.Ссылка, ...) не будет работать для новых объектов, которые еще не записаны в базу. Именно поэтому использование временных адресов (e1cib/tempstorage/...) является единственным надежным способом отображения фото до нажатия кнопки «Записать».
2. Интерактивность: Браузеры блокируют программное открытие файлов. Поэтому вызов ПоместитьФайлыНаСервер всегда должен быть инициирован непосредственным действием пользователя (нажатием кнопки).
3. Уникальный идентификатор: Всегда передавайте УникальныйИдентификатор формы в методы помещения файлов. Это гарантирует, что временное хранилище будет «привязано» к текущей форме и не очистится преждевременно при серверном вызове.
Мы проанализировали ситуацию и выяснили, что прямой перенос данных типа ХранилищеЗначения в реквизиты формы невозможен. Для корректной работы в веб-клиенте следует использовать комбинированный подход: хранить временные адреса строк в экранных реквизитах для визуализации и преобразовывать их в двоичные данные только в момент записи объекта на сервере. Также крайне рекомендуется рассмотреть вынос медиа-данных в отдельные справочники, чтобы обеспечить высокую производительность системы в будущем.