При разработке интеграционных механизмов, обмене данными по API или сериализации данных в формат Base64 программисты часто сталкиваются с необходимостью конвертировать текстовую информацию в двоичные данные без лишнего обращения к жесткому диску. В современных версиях платформы «1С:Предприятие 8» для этих целей идеально подходит связка объектов ПотокВПамяти и ЗаписьТекста. Однако при переносе или выполнении подобного кода в более старых информационных базах (например, работающих под управлением платформы версии 8.3.9) разработчики неожиданно сталкиваются с неприятной ошибкой: «Ошибка при вызове конструктора (ЗаписьТекста) из-за: Ошибка доступа к файлу 'ПотокВПамяти'».
В рамках этой статьи мы подробно разберем, почему возникает эта ошибка, как на нее влияет версия платформы и режим совместимости конфигурации, а также рассмотрим несколько практических способов ее обхода, включая высокопроизводительные варианты без использования временных файлов на диске.
Для того чтобы понять природу возникновения ошибки, проанализируем, как ведет себя платформа «1С:Предприятие» разных версий при работе со встроенными классами потоков. Механизмы низкоуровневой работы с потоками (такие как ПотокВПамяти, БуферДвоичныхДанных, ЗаписьДанных и ЧтениеДанных) впервые появились в платформе начиная с версии 8.3.9.
Однако на момент выпуска версии 8.3.9 классические объекты платформы для работы с файловой системой и текстом еще не были полностью интегрированы с новой подсистемой потоков. Конструктор объекта ЗаписьТекста в версии 8.3.9 ожидал получить в качестве первого параметра исключительно строку, содержащую путь к файлу на диске. Передавать туда объект типа ПотокВПамяти было еще нельзя.
Когда выполнялся следующий код:
Птк = Новый ПотокВПамяти;
Текст = Новый ЗаписьТекста(Птк);
Платформа 1С версии 8.3.9 пыталась неявно преобразовать переданный объект типа ПотокВПамяти к строковому типу. Результатом такого приведения становилась строка "ПотокВПамяти". Далее конструктор пытался инициализировать запись текстового файла с именем "ПотокВПамяти" в текущем рабочем каталоге операционной системы. Так как у платформы не было прав на создание файла с таким именем в системном каталоге, либо путь определялся некорректно, операционная система возвращала ошибку доступа, которую мы и видим в логах: «Ошибка доступа к файлу 'ПотокВПамяти'».
Полная поддержка работы с объектами потоков в конструкторах высокоуровневых объектов, таких как ЗаписьТекста, ЧтениеТекста, ЗаписьJSON, ЧтениеJSON, ЗаписьXML и ЗаписьZipФайла, была добавлена только в версии платформы 8.3.10.2168. Именно начиная с этой версии код начинает работать корректно и без ошибок.
Очень важный нюанс, о котором часто забывают разработчики: даже если вы обновите технологическую платформу сервера и клиента до актуальной версии (например, до 8.3.15, 8.3.22 или выше), исходный код все равно может продолжать выдавать ошибку доступа к файлу. Происходит это из-за настроек конфигурации.
Если в свойствах вашей конфигурации (в дереве метаданных в конфигураторе) свойство «Режим совместимости» установлено в значение «Версия 8.3.9» или ниже, платформа будет искусственно эмулировать поведение старой версии. В этом режиме конструктор ЗаписьТекста по-прежнему будет принимать только строковый путь к файлу и игнорировать переданный поток, вызывая ту же самую ошибку. Чтобы функционал заработал в штатном режиме, необходимо, чтобы режим совместимости конфигурации был установлен в значение «Версия 8.3.10» или выше (либо полностью отключен).
Что делать, если вам необходимо обеспечить работоспособность кода в старой версии платформы 8.3.9 или при включенном режиме совместимости с версией 8.3.9, но при этом вы не хотите использовать медленные дисковые операции записи временных файлов? Лучший выход — использовать специализированный объект ЗаписьДанных, который изначально умел работать с потоками в оперативной памяти.
Давайте подробно рассмотрим, как переписать исходный алгоритм кодирования строки в Base64 с использованием объекта ЗаписьДанных. Этот метод работает полностью в оперативной памяти, чрезвычайно быстр и абсолютно безопасен с точки зрения прав доступа к файловой системе сервера.
Выполним следующие шаги:
ПотокВПамяти для временного хранения байтов.ЗаписьДанных, передав в его конструктор наш поток и явно указав кодировку (например, UTF-8).ЗаписатьСимволы().Посмотрим на готовый пример программного кода:
// Исходная строка для кодирования
стр = "{""action"":""ping"",""status"":""active""}";
Результ = "";
// Создаем поток в оперативной памяти
Птк = Новый ПотокВПамяти;
// Используем ЗаписьДанных вместо ЗаписьТекста для совместимости с 8.3.9
Запись = Новый ЗаписьДанных(Птк, КодировкаТекста.UTF8);
Запись.ЗаписатьСимволы(стр);
Запись.Закрыть();
// Получаем двоичные данные из закрытого потока
ДД = Птк.ЗакрытьИПолучитьДвоичныеДанные();
// Кодируем результат в формат Base64
Результ = Base64Строка(ДД);
Этот алгоритм работает идентично исходному коду, но при этом он полностью совместим как со старыми релизами 8.3.9, так и со всеми последующими версиями платформы «1С:Предприятие 8».
Если по каким-то причинам использование низкоуровневых потоков вам не подходит или вам требуется гарантировать стандартное поведение платформы при записи файлов (например, автоматическую простановку меток порядка байтов BOM), можно использовать проверенный временем классический способ с созданием временного файла на диске.
Проанализируем этот подход по шагам:
ПолучитьИмяВременногоФайла().ЗаписьТекста, передав туда путь к временному файлу.ДвоичныеДанные.УдалитьФайлы(), чтобы не засорять жесткий диск сервера.Реализуем данный алгоритм на встроенном языке 1С:
стр = "{""action"":""ping"",""status"":""active""}";
Результ = "";
// Получаем путь к временному файлу на сервере
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("txt");
// Записываем текст в файл
Текст = Новый ЗаписьТекста(ИмяВременногоФайла, КодировкаТекста.UTF8);
Текст.Записать(стр);
Текст.Закрыть();
// Считываем файл в двоичные данные
ДД = Новый ДвоичныеДанные(ИмяВременногоФайла);
// Преобразуем в Base64
Результ = Base64Строка(ДД);
// Обязательно удаляем временный файл
Попытка
УдалитьФайлы(ИмяВременногоФайла);
Исключение
// Обработка возможных ошибок удаления файла
КонецПопытки;
Важное предупреждение: Использование временных файлов создает дополнительную нагрузку на дисковую подсистему сервера. Если данный код выполняется очень часто (например, в высоконагруженном веб-сервисе или при обработке сотен тысяч позиций маркировки товаров), это может привести к существенному замедлению работы системы. Старайтесь отдавать предпочтение Решению №1 (работе в оперативной памяти).
При переходе от дисковых файлов к операциям в оперативной памяти разработчики часто сталкиваются с неочевидным поведением: результирующая Base64-строка может отличаться по длине и содержанию, хотя исходный текст был абсолютно одинаковым. В чем кроется тайна этого явления?
Дело в маркере порядка байтов (BOM - Byte Order Mark). Это специальная сигнатура из трех байтов (EF BB BF для кодировки UTF-8), которая указывает программам на то, что файл записан в кодировке UTF-8.
ЗаписьТекста, платформа 1С по умолчанию всегда записывает маркер BOM в начало файла. В результате ваши двоичные данные будут содержать эти дополнительные 3 байта в самом начале, и Base64-строка будет длиннее.ПотокВПамяти и конструктор ЗаписьТекста(Поток) (доступный с версии 8.3.10) у разработчика появляется возможность управлять этим поведением с помощью специального булева параметра ЗаписатьBOM. По умолчанию при работе с потоками этот параметр равен Ложь (то есть BOM не пишется).ЗаписьДанных (Решение №1) маркер BOM также по умолчанию не записывается в поток.Если внешняя система (приемник вашей Base64-строки) строго требует отсутствия BOM (что крайне характерно для современных веб-сервисов и REST API), то использование потоков без BOM является единственно верным решением. Если же принимающей стороне обязательно нужен BOM, и вы используете Решение №1, вам придется вручную записать соответствующую сигнатуру в поток перед выводом основного текста.