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