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