При интеграции систем «1С:Предприятие» со сторонними веб-сервисами и мобильными приложениями разработчики регулярно сталкиваются с необходимостью передачи бинарных данных (картинок, PDF-документов, архивов) внутри формата JSON. Для этого двоичные данные кодируют в текстовую строку формата Base64.
Однако при формировании пакета JSON многие сталкиваются с неприятной особенностью: в результирующей строке JSON внезапно появляются экранированные последовательности переноса строки \r\n (или \\r\\n в итоговом тексте). Это приводит к тому, что внешние веб-сервисы не могут распарсить полученный JSON или корректно декодировать файл. В этой статье мы подробно разберем, почему возникает данная проблема, почему стандартные настройки сериализатора JSON не помогают и как решить эту задачу изящно и эффективно.
Рассмотрим типичный код, который часто пишут разработчики и который приводит к возникновению описываемой проблемы:
СтруктураНаСервер = Новый Структура("Data,Extension",
Base64Строка(Выборка.ДвоичныеДанныеФайла.Получить()),
Выборка.Расширение);
ЗаписьДжсон = Новый ЗаписьJSON;
ЗаписьДжсон.УстановитьСтроку();
ЗаписатьJSON(ЗаписьДжсон, СтруктураНаСервер);
СтрокаРезультат = ЗаписьДжсон.Закрыть();
При анализе полученной переменной СтрокаРезультат обнаруживается, что длинное текстовое поле с кодом Base64 разбито на фрагменты управляющими символами \r\n. Попытки отключить переносы в самом объекте ЗаписьJSON (например, через свойства ПараметрыЗаписиJSON) не дают никакого результата. Для детального анализа структуры строки и проверки пакетов можно использовать инструмент для форматирования и валидации JSON в 1С.
Дело в том, что системная функция 1С Base64Строка() работает строго по стандарту RFC 2045 (MIME). Исторически этот стандарт создавался для передачи почтовых сообщений, где существовали жесткие ограничения почтовых шлюзов на длину одной строки (не более 76 символов). Поэтому функция Base64Строка() принудительно разбивает результирующую непрерывную последовательность байтов на строки длиной по 72–76 символов, вставляя в места переноса символы возврата каретки и перевода строки (Символы.ВК + Символы.ПС).
Когда мы передаем полученную строку в структуру, а затем вызываем метод ЗаписатьJSON(), платформа воспринимает её как обычное текстовое значение. Чтобы сохранить структуру данных, сериализатор JSON обязан экранировать все спецсимволы, превращая реальные переносы строк в последовательность символов \r\n. Таким образом, настройки форматирования JSON никак не влияют на внутреннее содержимое строковых переменных.
Давайте разберем три практических способа решения этой проблемы.
Современные версии платформы «1С:Предприятие 8.3» обладают мощным встроенным механизмом сериализации. Нам больше не нужно вручную кодировать двоичные данные в Base64 перед помещением в структуру — платформа умеет делать это автоматически под капотом!
Проанализируем, как будет выглядеть правильный и лаконичный код:
// Получаем объект ДвоичныеДанные напрямую, без преобразования в Base64Строка
ДвоичныеДанныеФайла = Выборка.ДвоичныеДанныеФайла.Получить();
СтруктураНаСервер = Новый Структура;
СтруктураНаСервер.Вставить("Data", ДвоичныеДанныеФайла);
СтруктураНаСервер.Вставить("Extension", Выборка.Расширение);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
// Платформа сама сериализует ДвоичныеДанные в чистый Base64 без переносов строк
ЗаписатьJSON(ЗаписьJSON, СтруктураНаСервер);
СтрокаJSON = ЗаписьJSON.Закрыть();
При таком подходе платформа 1С самостоятельно преобразует объект типа ДвоичныеДанные в сплошную, непрерывную строку Base64, не содержащую никаких сервисных символов переноса строки. Это самый производительный и современный способ, который экономит оперативную память и процессорное время.
Если вам по каким-то причинам обязательно нужно получить саму строку Base64 в коде (например, для выполнения дополнительных манипуляций с текстом или для отправки через HTTP-запрос без использования структуры JSON), мы можем применить альтернативную системную функцию XMLСтрока().
Эта функция преобразует двоичные данные в формат Base64 согласно стандарту XML Schema (тип xs:base64Binary). Данный стандарт не накладывает ограничений на длину строки, поэтому на выходе мы сразу получаем чистую строку без переносов. Посмотрим на пример кода:
ДвоичныеДанныеФайла = Выборка.ДвоичныеДанныеФайла.Получить();
// Получаем чистую строку Base64 без переносов
ЧистаяСтрокаBase64 = XMLСтрока(ДвоичныеДанныеФайла);
СтруктураНаСервер = Новый Структура;
СтруктураНаСервер.Вставить("Data", ЧистаяСтрокаBase64);
СтруктураНаСервер.Вставить("Extension", Выборка.Расширение);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, СтруктураНаСервер);
СтрокаJSON = ЗаписьJSON.Закрыть();
Если принимающей стороной также является система 1С, то обратное декодирование полученной строки можно выполнить с помощью парной функции XMLЗначение():
ДвоичныеДанные = XMLЗначение(Тип("ДвоичныеДанные"), СтрокаBase64);
Если в вашем решении жестко завязано использование стандартной функции Base64Строка(), то единственным выходом остается принудительное удаление символов возврата каретки и перевода строки перед сериализацией.
Важный нюанс: распространенной ошибкой является удаление только символа перевода строки Символы.ПС. При этом в тексте остаются невидимые символы возврата каретки Символы.ВК, которые сериализатор JSON преобразует в последовательность \r, что по-прежнему будет портить валидность данных для многих парсеров. Очищать строку необходимо от обоих спецсимволов.
Разберем пример безопасной очистки строки:
ИсходнаяСтрокаBase64 = Base64Строка(Выборка.ДвоичныеДанныеФайла.Получить());
// Последовательно удаляем ВК (возврат каретки) и ПС (перевод строки)
ЧистаяСтрока = СтрЗаменить(ИсходнаяСтрокаBase64, Символы.ВК, "");
ЧистаяСтрока = СтрЗаменить(ЧистаяСтрока, Символы.ПС, "");
СтруктураНаСервер = Новый Структура("Data,Extension", ЧистаяСтрока, Выборка.Расширение);
Предупреждаем: данный метод крайне не рекомендуется использовать для обработки файлов больших размеров (более 10–15 мегабайт). Поскольку строки в платформе 1С являются неизменяемыми типами данных, функция СтрЗаменить() при каждом вызове создает в оперативной памяти полную копию строки. Для тяжелых файлов это приведет к лавинообразному потреблению ОЗУ и замедлению работы сервера.
Для наглядности сравним все три подхода, чтобы вы могли выбрать оптимальный вариант для вашего проекта:
Символы.ПС, так и от Символы.ВК.