При разработке систем электронного документооборота или архивов файлов в среде 1С:Предприятие 8.3 часто возникает задача сохранения бинарных данных (сканов, документов, картинок) непосредственно в информационную базу. Оптимальным объектом для хранения таких метаданных является Регистр сведений, где одним из ресурсов выступает поле с типом ХранилищеЗначения.
Однако начинающие разработчики часто сталкиваются с тем, что данные, выбранные на клиенте, «теряются» при записи или вовсе не попадают в базу данных. В этой статье мы подробно разберем, как правильно организовать процесс передачи файла с диска пользователя в ресурс регистра сведений, проанализируем типичные ошибки и рассмотрим современные стандарты разработки.
Основная сложность заключается в том, что реквизиты с типом ХранилищеЗначения не могут быть напрямую отображены на управляемой форме. Платформа 1С не передает содержимое бинарного хранилища между клиентом и сервером в составе основного реквизита формы Объект (или Запись в случае регистра). Это сделано для оптимизации трафика: если бы файл весом 50 Мб каждый раз «гонялся» туда-обратно при любом изменении на форме, работа системы стала бы невозможной.
Рассмотрим ситуацию: у нас есть регистр ХранилищеФайловДокументов с измерением Документ и ресурсом ДанныеЗагруженогоФайла. Нам необходимо загрузить файл через диалог и сохранить его.
Первым делом нам нужно получить данные с локального компьютера пользователя. В современных конфигурациях крайне рекомендуется использовать асинхронные методы, чтобы не блокировать интерфейс и обеспечить работу в веб-клиенте. Если же файлы должны поступать в систему из внешних источников, используется программа для автоматического сбора данных из почты, FTP и HTTP.
Разберем алгоритм: мы не просто получаем двоичные данные, а сразу помещаем их во Временное хранилище. Это специальная область памяти на сервере, доступ к которой осуществляется по уникальному адресу-строке.
Для этого на форме нам понадобится реквизит (переменная) с типом Строка, например, АдресФайлаВоВременномХранилище. Посмотрим на пример кода выбора файла:
&НаКлиенте
Асинхронная Процедура ЗагрузитьФайл(Команда)
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Фильтр = "Все файлы (*.*)|*.*";
// Используем асинхронный метод выбора
ВыбранныеФайлы = Ждать Диалог.ВыбратьАсинх();
Если ВыбранныеФайлы <> Неопределено Тогда
ПутьКФайлу = ВыбранныеФайлы[0];
// Помещаем файл во временное хранилище
РезультатПомещения = Ждать ПоместитьФайлНаСерверАсинх(,,, ПутьКФайлу, УникальныйИдентификатор);
Если РезультатПомещения <> Неопределено Тогда
// Сохраняем адрес в реквизит формы для последующей записи
АдресФайлаВоВременномХранилище = РезультатПомещения.Адрес;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Когда пользователь нажимает кнопку «Записать и закрыть», срабатывают стандартные механизмы платформы. Чтобы наши данные из временного хранилища попали в ресурс регистра, нам нужно перехватить процесс записи на сервере. Для этого идеально подходит обработчик события формы ПередЗаписьюНаСервере.
Важный нюанс: часто разработчики пытаются манипулировать параметрами записи или напрямую обращаться к реквизитам формы. Правильный подход — изменять непосредственно ТекущийОбъект, который платформа подготовила для записи в базу данных.
Разберем, как это реализовать:
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
// Проверяем, был ли выбран новый файл (заполнен ли адрес)
Если ЭтоАдресВременногоХранилища(АдресФайлаВоВременномХранилище) Тогда
// Получаем двоичные данные из хранилища по адресу
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресФайлаВоВременномХранилище);
// Помещаем данные в ресурс объекта через конструктор ХранилищеЗначения
// Второй параметр "Новый СжатиеДанных(9)" позволяет оптимизировать размер БД
ТекущийОбъект.ДанныеЗагруженогоФайла = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));
// После извлечения данных адрес можно очистить,
// но платформа сделает это сама при закрытии формы
УдалитьИзВременногоХранилища(АдресФайлаВоВременномХранилище);
АдресФайлаВоВременномХранилище = "";
КонецЕсли;
КонецПроцедуры
Проанализируем ситуацию, описанную в сообщениях форума, где автор жаловался, что «ничего не происходит».
ПередЗаписью: В сообщении №2 пользователь пытался сам вызвать ПередЗаписьюНаСервере. Это грубая ошибка. Данный обработчик должен вызываться самой платформой. Для этого нужно зайти в свойства формы, найти событие «Перед записью на сервере» и нажать на лупу, чтобы создать корректную связь (делегат).ПараметрыЗаписи для передачи данных: Коллекция ПараметрыЗаписи предназначена для передачи инструкций (флагов) о том, как записывать, а не что записывать. Для изменения данных записи нужно использовать ТекущийОбъект.ПолучитьДвоичныеДанные() возвращал пустой объект. Всегда добавляйте проверку на размер полученных данных перед созданием ХранилищеЗначения.Документ) заполнено. Без ключевых полей (измерений) 1С не сможет создать уникальную запись в таблице регистра.Хранение файлов в регистре сведений удобно тем, что данные всегда попадают в бэкап всей базы. Однако при больших объемах (терабайты данных) база начинает «тормозить», а регламентные операции (индексация, ТИС) длятся вечно. Чтобы регламентное обслуживание не мешало работе пользователей, можно настроить автоматическое тестирование и архивацию баз 1С по расписанию.
Выясним, какие есть альтернативы:
СжатиеДанных при создании ХранилищеЗначения. Для текстовых файлов (XML, JSON, LOG) это дает экономию места до 90%.Подведем итог: для успешного сохранения файла в регистр сведений необходимо использовать связку Клиент (выбор и помещение во временное хранилище) -> Сервер (получение из хранилища и запись в ТекущийОбъект). Именно такая схема гарантирует стабильную работу управляемого приложения.