При интеграции систем часто возникает ситуация, когда внешние веб-сервисы, HTTP-сервисы или API присылают XML-данные в виде обычной текстовой строки. Если для дальнейшей обработки данных используется стандартный механизм загрузки (например, популярная обработка «Универсальный обмен данными в формате XML»), нам обязательно требуется передать ей физический файл на диске. Прямая передача строки в такие обработчики невозможна.
В этой статье мы подробно разберем, как решить эту задачу в среде 1С:Предприятие. Мы рассмотрим несколько надежных способов преобразования XML-строки в файл, научимся правильно работать с кодировками, корректно генерировать пути к файлам и вовремя уничтожать временные данные, чтобы не перегружать жесткий диск сервера.
Прежде чем записывать данные, необходимо определить, куда именно на диске мы будем складывать наш файл. Использование жестко прописанных путей вида "C:\temp\import.xml" — это серьезная архитектурная ошибка. На сервере 1С у службы, под которой запущен процесс, может просто не быть прав на запись в корень диска C. Кроме того, при одновременной работе нескольких пользователей или фоновых заданий файлы с фиксированными именами начнут перезаписывать друг друга, приводя к непредсказуемым ошибкам в данных.
Для создания уникального и безопасного пути мы всегда должны использовать встроенную функцию платформы ПолучитьИмяВременногоФайла(). Давайте посмотрим, как это делается:
// Платформа сгенерирует уникальный путь в системном каталоге временных файлов
// и автоматически добавит расширение ".xml"
ИмяФайла = ПолучитьИмяВременногоФайла("xml");
Важно понимать, что сама функция ПолучитьИмяВременногоФайла() физический файл на диске не создает. Она лишь резервирует уникальный путь в файловой системе. Запись данных по этому пути — задача нашего программного кода.
Поскольку XML по своей сути является текстовым форматом, использование специализированного объекта ЗаписьТекста — это наиболее логичный и простой путь. Этот объект оптимизирован для работы со строками и позволяет четко контролировать кодировку результирующего файла.
Давайте проанализируем пример кода для записи нашей строки:
ИмяФайла = ПолучитьИмяВременногоФайла("xml");
// Создаем объект записи и указываем кодировку UTF-8 (наиболее частый стандарт для XML)
ЗаписьТекста = Новый ЗаписьТекста(ИмяФайла, КодировкаТекста.UTF8);
// Записываем XML-строку, полученную из веб-сервиса
ЗаписьТекста.Записать(Upload_xml);
// Обязательно закрываем файл, чтобы сохранить изменения и освободить ресурсы в ОС
ЗаписьТекста.Закрыть();
Этот способ работает быстро, надежно и отлично подходит для 95% повседневных задач обмена данными.
Многие начинающие разработчики ошибочно полагают, что объект ТекстовыйДокумент жестко ограничен форматом .txt. На самом деле метод Записать() данного объекта абсолютно не контролирует расширение файла. Мы можем легко передать туда путь с расширением .xml, и платформа сохранит файл в нужном нам формате.
Разберем, как выглядит этот вариант программного кода:
ИмяФайла = ПолучитьИмяВременногоФайла("xml");
ТекстовыйДок = Новый ТекстовыйДокумент;
// Помещаем XML-строку в текстовое поле документа
ТекстовыйДок.УстановитьТекст(Upload_xml);
// Записываем файл на диск, явно указав требуемую кодировку
ТекстовыйДок.Записать(ИмяФайла, КодировкаТекста.UTF8);
Этот вариант также полностью работоспособен, однако он требует чуть больше оперативной памяти, так как объект ТекстовыйДокумент предназначен для интерактивной работы с текстом и выполняет дополнительные внутренние операции по разбиению строк.
Если ваша строка содержит специфические непечатаемые символы, спецсимволы или вы хотите полностью исключить влияние платформы 1С на исходную кодировку текста, идеальным решением будет преобразование строки в двоичные данные.
Рассмотрим пример реализации:
ИмяФайла = ПолучитьИмяВременногоФайла("xml");
// Преобразуем строковые данные напрямую в бинарный формат.
// Параметр "Ложь" в конце указывает, что нам не нужно добавлять BOM (Byte Order Mark) в начало файла
ДвоичныеДанные = ПолучитьДвоичныеДанныеИзСтроки(Upload_xml, КодировкаТекста.UTF8, Ложь);
// Записываем полученный бинарный поток на диск
ДвоичныеДанные.Записать(ИмяФайла);
Использование ДвоичныеДанные гарантирует, что файл запишется байт-в-байт в точности так, как его вернул веб-сервис.
При записи XML-файлов всегда обращайте внимание на так называемую XML-декларацию (самую первую строку в вашем XML-тексте). Она содержит указание для парсера, в какой кодировке ожидать символы.
Правило соответствия кодировок:
<?xml version="1.0" encoding="utf-8"?>, то при записи файла в 1С мы обязаны использовать параметр КодировкаТекста.UTF8.<?xml version="1.0" encoding="windows-1251"?>, мы должны использовать КодировкаТекста.ANSI (или явно прописать "windows-1251").Что произойдет при ошибке? Если кодировка самого файла на диске (физические байты) не совпадет со значением атрибута encoding внутри файла, любой XML-парсер (включая обработку «Универсальный обмен данными») выдаст фатальную ошибку чтения и прекратит импорт данных.
Поскольку код интеграции обычно выполняется в контексте серверных вызовов (например, внутри модуля веб-сервиса), все создаваемые файлы сохраняются во временной папке на сервере 1С. В отличие от клиентского компьютера, серверный рабочий процесс 1С может непрерывно работать месяцами без перезагрузки.
Если мы забудем удалить временный файл после окончания загрузки, эти файлы начнут быстро накапливаться, что в конечном итоге приведет к исчерпанию свободного места на системном диске сервера. Всегда используйте конструкцию Попытка...Исключение для гарантированного удаления временного файла:
// ... Здесь выполняется код загрузки данных из временного файла ...
Попытка
УдалитьФайлы(ИмяФайла);
Исключение
// Записываем информацию в Журнал Регистрации, если файл оказался заблокирован системой
ЗаписьЖурналаРегистрации("ОбменДанными.ОчисткаВременныхФайлов",
УровеньЖурналаРегистрации.Предупреждение, , ,
"Не удалось удалить временный файл: " + ИмяФайла + Символы.ПС + ОписаниеОшибки());
КонецПопытки;
В контексте поставленной автором темы задачи файл действительно необходим, так как стандартный функционал универсальной выгрузки-загрузки жестко ориентирован на работу с файловой системой.
Однако, если перед вами стоит задача самостоятельно разобрать полученную XML-строку без использования готовых обработок, создавать промежуточный файл на диске нет никакой необходимости. Объект ЧтениеXML умеет работать напрямую с оперативной памятью компьютера.
Давайте посмотрим, как реализовать чтение XML-данных «на лету»:
ЧтениеXML = Новый ЧтениеXML;
// Вместо открытия файла инициализируем чтение напрямую из текстовой переменной
ЧтениеXML.УстановитьСтроку(Upload_xml);
// Последовательно читаем узлы XML-документа
Пока ЧтениеXML.Прочитать() Цикл
// Выполняем разбор элементов
Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
ИмяЭлемента = ЧтениеXML.Имя;
// Ваша логика обработки элемента...
КонецЕсли;
КонецЦикла;
// Закрываем чтение для освобождения оперативной памяти
ЧтениеXML.Закрыть();
Использование метода УстановитьСтроку() позволяет значительно ускорить работу алгоритма, так как мы полностью исключаем медленные дисковые операции чтения и записи (I/O) и экономим системные ресурсы сервера.