При пакетной обработке или интеграции часто приходится сталкиваться с чтением огромных XML-файлов (например, выгрузок данных из «1С:Конвертация данных» объемом от десятков до сотен мегабайт и миллионами узлов). При использовании стандартного объекта ЧтениеXML файл обрабатывается строго последовательно. Узнать общее количество узлов заранее стандартными свойствами парсера невозможно, что усложняет построение привычного индикатора прогресса (прогресс-бара).
Разберем, почему классические подходы с загрузкой документа целиком могут привести к аварийной остановке программы, и рассмотрим надежные практические варианты реализации индикации хода процесса.
Первая идея, которая часто возникает у разработчиков — использовать ДокументDOM и ПостроительDOM, так как в модели DOM присутствуют штатные методы подсчета и выборки узлов. Однако проанализируем ограничения этого подхода:
Именно поэтому стандарт 1С рекомендует применять объектную модель DOM только для сравнительно небольших файлов (ориентировочно до 50–100 МБ в зависимости от вложенности и разрядности системы). Для объемных файлов следует применять последовательное или потоковое чтение.
Начиная с версии платформы 8.3.10, объект ЧтениеXML поддерживает инициализацию через потоки данных с помощью метода ОткрытьПоток(). Это позволяет привязать прогресс-бар к физическому объему обработанных данных в байтах, не считывая файл целиком в оперативную память.
Разберем по шагам, как устроен этот механизм:
ФайловыйПоток в режиме чтения.Размер().ЧтениеXML.ОткрытьПоток().Поток.ТекущаяПозиция().(ТекущаяПозиция / Размер) * 100.Посмотрим на пример реализации данного алгоритма:
Поток = Новый ФайловыйПоток(ИмяФайла, РежимОткрытияФайла.Открыть, ДоступКФайлу.Чтение);
РазмерФайла = Поток.Размер();
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьПоток(Поток);
ПроцентПредыдущий = 0;
Пока ЧтениеXML.Прочитать() Цикл
// Обработка данных узла
Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
// Выполняем полезную работу
КонецЕсли;
// Расчет процента выполнения
Если РазмерФайла > 0 Тогда
ТекущийПроцент = Цел((Поток.ТекущаяПозиция() / РазмерФайла) * 100);
Если ТекущийПроцент > ПроцентПредыдущий Тогда
ПроцентПредыдущий = ТекущийПроцент;
// Оповещение интерфейса или фиксация прогресса
КонецЕсли;
КонецЕсли;
КонецЦикла;
ЧтениеXML.Закрыть();
Поток.Закрыть();
Так как ЧтениеXML буферизирует чтение с диска, смещение в потоке прирастает блоками, чего вполне достаточно для плавного обновления шкалы выполнения.
Если для индикатора принципиально отображать именно количество бизнес-объектов (например, справочников и документов из файла «Конвертации данных»), а не абстрактные байты, эффективным решением будет предварительный проход.
Основное время при загрузке данных (до 95–99%) тратится не на парсинг XML, а на поиск ссылок, проведение проверок и запись объектов в базу данных. «Холостой» первый проход парсером без записи объектов занимает считанные секунды даже на больших объемах.
Рассмотрим порядок действий:
ЧтениеXML в первом цикле и подсчитываем только открывающие теги ключевых сущностей (например, узлы с именем Объект).Посмотрим на пример предварительного подсчета целевых узлов:
ВсегоОбъектов = 0;
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ИмяФайла);
// Первый быстрый проход: только подсчет
Пока ЧтениеXML.Прочитать() Цикл
Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXML.Имя = "Объект" Тогда
ВсегоОбъектов = ВсегоОбъектов + 1;
КонецЕсли;
КонецЦикла;
ЧтениеXML.Закрыть();
// Второй проход: загрузка с известным общим количеством
ЧтениеXML.ОткрытьФайл(ИмяФайла);
ОбработаноОбъектов = 0;
Пока ЧтениеXML.Прочитать() Цикл
Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXML.Имя = "Объект" Тогда
// Чтение и запись объекта в базу
ОбработаноОбъектов = ОбработаноОбъектов + 1;
КонецЕсли;
КонецЦикла;
ЧтениеXML.Закрыть();
Если структура файла неоднородна, модифицировать алгоритм ради предварительного чтения нецелесообразно, а задача выполняется интерактивно, оптимальным решением является отказ от классического процента выполнения в пользу динамического счетчика обработанных элементов.
В типовых решениях «1С» для аналогичных длительных процедур используется индикатор без конечного значения (анимация ожидания) совместно с текстовым описанием текущего состояния: например, «Обработано объектов: N». Это снимает накладные расходы на предварительный анализ файла и исключает риск зависания системы.
Реализуя отображение хода загрузки в управляемом приложении, важно помнить об архитектурных ограничениях:
ФоновыеЗадания).