Интеграция систем 1С с внешними веб-сервисами, WMS-системами и REST API — одна из самых частых задач современного разработчика. Однако при отсутствии четкого технического задания (ТЗ) и жестких лимитах времени легко совершить архитектурные ошибки, которые приведут к зависанию системы, искажению данных или падению рабочего процесса сервера 1С. Особенно остро эти проблемы проявляются при передаче файлов больших объемов (от 10-50 МБ и выше).
В этой статье мы подробно разберем, как реализовать отправку XML-файла POST-запросом на платформе 1С:Предприятие 8.3. Мы проанализируем частые ошибки разработчиков, обсудим вопросы кодировок, лимитов оперативной памяти, маркеры BOM, а также напишем надежный, современный и производительный код для интеграции.
Прежде чем перейти к написанию готового шаблона, давайте детально проанализируем ошибки, которые часто встречаются в legacy-коде и на форумах разработчиков.
1. Ручной расчет заголовка Content-Length
Многие разработчики по старой привычке пытаются вручную вычислить размер отправляемого тела запроса и передать его в заголовках. Для этого они используют функцию СтрДлина(). Это приводит к критической ошибке:
СтрДлина() возвращает количество символов в строке.Content-Length требует указания размера тела запроса строго в байтах.Решение: В современной платформе 1С 8.3 при использовании объектов HTTPЗапрос ручной расчет длины больше не требуется. Платформа самостоятельно вычисляет точный размер в байтах при вызове методов УстановитьТелоИзСтроки() или УстановитьИмяФайлаТела().
2. Использование строк в памяти для больших XML (от 10 МБ до 70 МБ и более)
В попытках сделать решение «без лишних файлов на диске» разработчики нередко считывают XML-файл полностью в строку и передают ее через метод УстановитьТелоИзСтроки(). Рассмотрим, почему для файлов объемом 70 МБ это гарантированный крах:
Решение: Использовать дисковую потоковую передачу (стриминг). Данные записываются во временный файл, а затем подключаются к запросу через метод УстановитьИмяФайлаТела(). Платформа будет транслировать файл в сеть небольшими порциями, не перегружая RAM.
3. Игнорирование проблемы маркера BOM (Byte Order Mark)
По умолчанию при записи файлов в UTF-8 платформа 1С может добавлять в начало файла невидимую метку BOM (последовательность байтов EF BB BF). Большинство современных веб-серверов и сторонних WMS-систем не ожидают увидеть эти символы в начале XML-пакета и возвращают ошибку разбора формата (например, 400 Bad Request или SAXParseException).
Решение: При записи XML-файла или формировании строки необходимо явно указывать платформе не использовать маркер BOM.
4. Ошибки инициализации защищенного соединения (SSL/HTTPS)
При создании объекта HTTPСоединение разработчики часто путают параметры протокола. Например, указывают шестым параметром значение Истина (использовать защищенное соединение), но при этом обращаются к адресу по обычному незащищенному протоколу http:// на нестандартном порту (например, 7000). Это вызывает ошибку на этапе сетевого рукопожатия (Handshake).
Решение: Четко разделять параметры подключения. Если URL начинается с http://, защищенное соединение использовать нельзя, а порт нужно передавать отдельным вторым параметром конструктора.
5. Некорректный заголовок Content-Type
Отправляя XML-данные, разработчики нередко оставляют в заголовках значение application/json или забывают указать кодировку. Веб-сервер получателя не сможет корректно интерпретировать пришедший поток байтов без правильного указания типа содержимого.
Решение: Для XML-запросов всегда используйте заголовок application/xml; charset=utf-8 или text/xml; charset=utf-8.
Учитывая все перечисленные нюансы, давайте спроектируем правильную архитектуру отправки данных.
Рассмотрим детально каждый шаг решения задачи отправки крупного XML-файла во внешнюю систему.
Шаг 1. Формирование XML во временный файл без BOM
Для того чтобы не держать весь объем данных в оперативной памяти, мы будем записывать XML напрямую во временный файл на диске с помощью специализированного объекта ЗаписьXML. Мы обязательно отключим маркер BOM с помощью объекта ПараметрыЗаписиXML.
Шаг 2. Инициализация HTTP-соединения
Мы разберем исходный URL получателя. Например: http://192.168.20.12:7000/cwms/ws/NOM.DEV
Шаг 3. Подготовка HTTP-запроса
Мы создадим объект HTTPЗапрос, укажем правильный заголовок Content-Type и свяжем наш временный файл с телом запроса методом УстановитьИмяФайлаТела().
Шаг 4. Отправка и обработка ответа
Мы выполним отправку метода POST. Полученный ответ сервера мы прочитаем прямо в строку с помощью современного метода ПолучитьТелоКакСтроку(), избегая создания лишних файлов для ответа на диске. В конце обязательно удалим созданный временный файл тела запроса, чтобы не засорять жесткий диск сервера.
Ниже представлен готовый к использованию программный код, написанный по всем стандартам разработки под управляемые и обычные формы 1С v8.3:
&НаСервере
Процедура ОтправитьБольшойXMLНаСервер()
// 1. Создаем временный файл для записи XML на диске (экономим RAM)
ПутьКФайлуXML = ПолучитьИмяВременногоФайла("xml");
ЗаписьXML = Новый ЗаписьXML;
// Настраиваем параметры записи: кодировка UTF-8, БЕЗ добавления BOM
ПараметрыЗаписи = Новый ПараметрыЗаписиXML("UTF-8", , Ложь);
ЗаписьXML.ОткрытьФайл(ПутьКФайлуXML, ПараметрыЗаписи);
// Записываем структуру XML документа
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьXML.ЗаписатьНачалоЭлемента("Root");
// Имитируем заполнение данными (в реальной задаче здесь будет ваш цикл)
Для Индекс = 1 По 1000 Цикл
ЗаписьXML.ЗаписатьНачалоЭлемента("Item");
ЗаписьXML.ЗаписатьАтрибут("Id", Строка(Индекс));
ЗаписьXML.ЗаписатьТекст("Тестовые данные для WMS системы № " + Строка(Индекс));
ЗаписьXML.ЗаписатьКонецЭлемента();
КонецЦикла;
ЗаписьXML.ЗаписатьКонецЭлемента(); // Root
ЗаписьXML.Закрыть();
// 2. Параметры подключения к серверу (разбираем исходный URL)
ИмяСервера = "192.168.20.12";
Порт = 7000;
АдресРесурса = "/cwms/ws/NOM.DEV";
Таймаут = 60; // Увеличиваем таймаут для больших файлов (в секундах)
// Так как протокол HTTP (не HTTPS), защищенное соединение не требуется (Параметр 6 = Неопределено)
Соединение = Новый HTTPСоединение(ИмяСервера, Порт, , , , Таймаут);
// 3. Создаем и заполняем HTTP запрос
Запрос = Новый HTTPЗапрос(АдресРесурса);
// Указываем корректный тип контента для XML данных
Запрос.Заголовки.Вставить("Content-Type", "application/xml; charset=utf-8");
// Передаем путь к файлу. Платформа сама посчитает Content-Length в байтах!
Запрос.УстановитьИмяФайлаТела(ПутьКФайлуXML);
// 4. Отправка запроса и получение ответа
Попытка
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
// Проверяем успешность выполнения запроса на уровне протокола HTTP
Если Ответ.КодСостояния = 200 Тогда
// Получаем текст ответа напрямую из памяти без промежуточных файлов
СтрокаОтветаСервера = Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
// Здесь выполняем обработку полученного ответа (например, чтение XML-ответа)
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Запрос успешно выполнен! Ответ сервера: " + СтрокаОтветаСервера);
Иначе
// Сервер вернул ошибку обработки (например, 400, 404, 500)
СтрокаОшибкиСервера = Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
ВызватьИсключение "Сервер вернул код состояния: " + Ответ.КодСостояния + ". Текст ошибки: " + СтрокаОшибкиСервера;
КонецЕсли;
Исключение
// Фиксируем системную или сетевую ошибку в Журнале регистрации
ЗаписатьЖурналРегистрации(
"Интеграция.ОтправкаXML",
УровеньЖурналаРегистрации.Ошибка,
,
,
"Произошла ошибка при отправке XML: " + ОписаниеОшибки()
);
ВызватьИсключение "Не удалось отправить данные. Подробности смотрите в Журнале регистрации.";
КонецПопытки;
// 5. Обязательная очистка временных ресурсов на диске
Попытка
УдалитьФайлы(ПутьКФайлуXML);
Исключение
// Ошибка удаления временного файла не должна прерывать основную логику
ЗаписатьЖурналРегистрации(
"Интеграция.ОчисткаФайлов",
УровеньЖурналаРегистрации.Предупреждение,
,
,
"Не удалось удалить временный файл: " + ПутьКФайлуXML + ". Ошибка: " + ОписаниеОшибки()
);
КонецПопытки;
КонецПроцедуры
Если вы гарантированно уверены, что размер генерируемого XML-документа никогда не превысит пары мегабайт, оперативная память сервера не пострадает от операций со строками. В этом случае мы можем отказаться от записи временного файла на диск и выполнить всю процедуру в оперативной памяти.
Для реализации этого сценария мы воспользуемся объектом ЗаписьXML с методом УстановитьСтроку(). Рассмотрим пример:
&НаСервере
Функция СформироватьXMLВСтроку()
ЗаписьXML = Новый ЗаписьXML;
// Открываем запись в строку без использования BOM маркера
ПараметрыЗаписи = Новый ПараметрыЗаписиXML("UTF-8", , Ложь);
ЗаписьXML.УстановитьСтроку(ПараметрыЗаписи);
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьXML.ЗаписатьНачалоЭлемента("Root");
ЗаписьXML.ЗаписатьНачалоЭлемента("SmallData");
ЗаписьXML.ЗаписатьТекст("Передача небольшого объема данных");
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.ЗаписатьКонецЭлемента();
Возврат ЗаписьXML.Закрыть(); // Возвращает готовую строку XML
КонецФункции
&НаСервере
Процедура ОтправитьМалыйXMLНаСервер()
XMLСтрокаДанных = СформироватьXMLВСтроку();
Соединение = Новый HTTPСоединение("192.168.20.12", 7000, , , , 30);
Запрос = Новый HTTPЗапрос("/cwms/ws/NOM.DEV");
Запрос.Заголовки.Вставить("Content-Type", "application/xml; charset=utf-8");
// Передаем строку напрямую. Платформа сама переведет ее в UTF-8 и рассчитает Content-Length в байтах
// Указываем ИспользованиеByteOrderMark.НеИспользовать, чтобы избежать ошибок парсинга на сервере
Запрос.УстановитьТелоИзСтроки(XMLСтрокаДанных, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
Попытка
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Если Ответ.КодСостояния = 200 Тогда
СтрокаОтвета = Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
// Обработка ответа...
КонецЕсли;
Исключение
// Обработка исключений...
КонецПопытки;
КонецПроцедуры
Мы проанализировали процесс отправки XML-файлов POST-запросом и выявили ключевые правила, которых следует придерживаться:
Content-Length вручную через СтрДлина(). Доверьте это платформе 1С.HTTPЗапрос.УстановитьИмяФайлаТела() с записью данных во временный файл. Это убережет сервер 1С от падения из-за нехватки оперативной памяти.Ложь в параметрах записи XML или ИспользованиеByteOrderMark.НеИспользовать при работе со строками.Используя предложенные шаблоны кода, вы сможете построить надежную и быструю интеграцию с любой WMS или учетной системой, экономя время и ресурсы вашего сервера.