Как правильно сохранить и прочитать XML-файл из Хранилища Значения в управляемых формах 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Разработка в среде управляемых форм 1С:Предприятия предъявляет жесткие требования к разграничению контекстов исполнения кода. Одной из частых проблем, с которой сталкиваются разработчики, является сохранение файлов (например, файлов выгрузки данных .xml) непосредственно в реквизит объекта с типом ХранилищеЗначения. Трудность заключается в том, что реквизиты этого типа недоступны напрямую на клиенте и не могут быть связаны с реквизитами формы в качестве данных.

В этой статье мы подробно разберем, как устроен жизненный цикл бинарных данных в управляемом приложении, рассмотрим по шагам процесс сохранения файлов, изучим современные потоковые методы чтения информации и обсудим архитектурные практики оптимизации базы данных.

Почему возникает проблема: клиент-серверная архитектура

Выясним причину, по которой стандартное присвоение пути к файлу реквизиту объекта вызывает ошибку. В управляемом приложении клиентский сеанс не имеет прямого доступа к СУБД, а серверный вызов не может обратиться к файловой системе локального компьютера пользователя. Реквизит типа ХранилищеЗначения физически находится в базе данных и обрабатывается исключительно на стороне сервера.

Более того, платформа не позволяет напрямую передать объект типа ХранилищеЗначения с клиента на сервер, так как данный тип не сериализуется для передачи по каналам связи между тонким клиентом и сервером. Для решения этой задачи используется промежуточное звено — Временное хранилище.

---

Пошаговое руководство по записи файла в Хранилище Значения

Рассмотрим классический и наиболее надежный алгоритм загрузки файла с локального компьютера пользователя и его сохранения в реквизит справочника.

Шаг 1. Выбор файла на клиенте и помещение во временное хранилище

Сначала нам необходимо получить двоичные данные файла на стороне клиента и временно загрузить их на сервер. В современных версиях платформы 1С для этого используется асинхронный метод НачатьПомещениеФайлаНаСервер(), который корректно работает во всех типах клиентов (включая веб-клиент) и не блокирует интерфейс пользователя.

Посмотрим на пример реализации интерактивного выбора файла на клиенте:


&НаКлиенте
Процедура ЗагрузитьФайлXML(Команда)
    
    // Настраиваем параметры диалога выбора файла
    ПараметрыДиалога = Новый ПараметрыДиалогаПомещенияФайлов("Выберите XML-файл поставщика", Ложь, "Файлы XML (*.xml)|*.xml");
    
    // Создаем описание оповещения для обработки результата загрузки
    ОписаниеОповещения = Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтаФорма);
    
    // Асинхронно отправляем файл на сервер
    НачатьПомещениеФайлаНаСервер(ОписаниеОповещения, , , , ПараметрыДиалога, УникальныйИдентификатор);
    
КонецПроцедуры

После того как пользователь выберет файл, платформа загрузит его во временное хранилище и вызовет процедуру обратного вызова. Обратите внимание, что мы передаем УникальныйИдентификатор формы — это гарантирует, что временный файл будет автоматически удален из оперативной памяти сервера после закрытия нашей формы.


&НаКлиенте
Процедура ПослеПомещенияФайла(Результат, ДополнительныеПараметры) Экспорт
    
    Если Результат <> Неопределено Тогда
        
        // Получаем уникальный адрес во временном хранилище
        АдресДвоичныхДанных = Результат.Адрес;
        
        // Помечаем форму как измененную, чтобы платформа знала о наличии несохраненных данных
        Модифицированность = Истина;
        
        ПоказатьПредупреждение(, "Файл успешно загружен и готов к сохранению!");
        
    КонецЕсли;
    
КонецПроцедуры

Для того чтобы адрес временного хранилища был доступен между различными процедурами формы, объявим переменную АдресДвоичныхДанных в качестве реквизита управляемой формы с типом Строка.

Шаг 2. Запись данных в объект на сервере

Теперь, когда бинарные данные нашего XML-файла находятся на сервере во временном хранилище, мы можем безопасно записать их в реквизит объекта. Наиболее подходящим местом для этого является обработчик события формы ПередЗаписьюНаСервере. В этот момент у нас есть прямой доступ к записываемому объекту базы данных через параметр ТекущийОбъект.

Разберем код серверной процедуры:


&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    
    // Проверяем, был ли загружен файл во временное хранилище
    Если ЗначениеЗаполнено(АдресДвоичныхДанных) Тогда
        
        // Извлекаем двоичные данные из временного хранилища
        ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(АдресДвоичныхДанных);
        
        Если ТипЗнч(ДвоичныеДанныеФайла) = Тип("ДвоичныеДанные") Тогда
            
            // Помещаем двоичные данные в ХранилищеЗначения реквизита объекта
            ТекущийОбъект.ФайлПоставщика = Новый ХранилищеЗначения(ДвоичныеДанныеФайла);
            
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

Этот подход изящно решает проблему отсутствия прямой связи реквизита базы данных с элементами формы. Данные передаются «транзитом» через оперативную память сервера непосредственно в момент записи элемента справочника или документа.

---

Пошаговое руководство по чтению XML-файла

Проанализируем обратную задачу: пользователь открывает существующий элемент справочника, и нам необходимо прочитать ранее сохраненный XML-файл для визуализации его структуры или последующей обработки.

Исторически разработчики часто использовали запись временного файла на жесткий диск сервера через метод Записать() объекта ДвоичныеДанные, после чего читали этот файл с диска. Однако запись на физический диск сервера снижает общую производительность системы и требует ручной очистки временных файлов. Мы применим современный и быстрый способ — чтение потока данных напрямую из оперативной памяти.

Выполним чтение в событии формы ПриЧтенииНаСервере:


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

Использование метода ОткрытьПотокДляЧтения() позволяет обрабатывать даже файлы большого объема практически мгновенно, поскольку операции ввода-вывода (I/O) производятся в RAM-буфере сервера приложений 1С, минуя дисковую подсистему.

---

Оптимизация производительности: Сжатие данных

По умолчанию конструктор Новый ХранилищеЗначения(Данные) применяет стандартный алгоритм сжатия платформы. Однако, поскольку XML-файлы представляют собой обычный текст, они обладают колоссальным потенциалом сжатия (вплоть до 90% от исходного объема).

Для экономии дискового пространства базы данных СУБД мы можем явно указать максимальный уровень сжатия данных при создании объекта ХранилищеЗначения. Для этого используется системный объект СжатиеДанных:


// Используем максимальную степень сжатия (значение 9)
СтепеньСжатия = Новый СжатиеДанных(9);
ТекущийОбъект.ФайлПоставщика = Новый ХранилищеЗначения(ДвоичныеДанныеФайла, СтепеньСжатия);

Это незначительно увеличит нагрузку на процессор сервера при записи и чтении, но существенно уменьшит размер физических таблиц базы данных на диске.

---

Важные архитектурные рекомендации

Хранение тяжелых бинарных файлов (особенно если их размер превышает несколько мегабайт) непосредственно внутри основных реквизитов справочников имеет скрытые архитектурные угрозы:

  1. Деградация производительности СУБД: При выполнении простых запросов к справочнику (например, для построения динамического списка или при поиске элементов) СУБД вынуждена считывать строки таблиц целиком. Наличие тяжелых полей типа Blob в основной таблице приводит к резкому замедлению сканирования данных.
  2. Разбухание резервных копий: База данных стремительно растет в объемах, что усложняет администрирование, резервное копирование и восстановление системы.

Для оптимизации этой структуры рекомендуется использовать один из следующих подходов:

---

Борьба с зависанием интерфейса при чтении больших XML

Если обрабатываемый XML-файл имеет значительный объем (десятки мегабайт), его парсинг в процедуре ПриЧтенииНаСервере неизбежно приведет к временному зависанию клиентского приложения. Пользователь увидит белый экран или надпись «Программа не отвечает».

Чтобы избежать блокировки интерфейса, используйте следующую асинхронную модель работы:

  1. В процедуре ПриЧтенииНаСервере не выполняйте парсинг файла. Ограничьтесь лишь получением информации о том, что файл прикреплен к объекту (например, установите булевый флаг на форме ФайлЗагружен = Истина).
  2. В обработчике формы ПриОткрытии на клиенте подключите однократный обработчик ожидания с задержкой в 0.1 секунды: ПодключитьОбработчикОжидания("НачатьАсинхронноеЧтениеФайла", 0.1, Истина);
  3. В процедуре обработчика ожидания запустите фоновое задание для парсинга XML, используя стандартные механизмы БСП (функция ДлительныеОперации.ЗапуститьВыполнениеВФоне()). Это позволит показать пользователю красивое окно ожидания с индикатором прогресса и кнопкой отмены, сохранив отзывчивость интерфейса системы.

Применение описанных методик позволит создавать быстрые, отказоустойчивые и современные интерфейсы интеграции в вашей конфигурации 1С:Предприятие.

← На главную