Как преобразовать массив байт (COMSafeArray) в ДвоичныеДанные без использования временного файла?

Программист IT и автоматизация бизнеса
← На главную

При работе с внешними источниками данных через COM-объекты, например, при получении данных из баз данных через ADODB, часто возникает задача обработки бинарных полей (изображений, файлов, документов). Стандартный подход — сохранить полученный массив байт во временный файл и затем прочитать его в 1С как объект ДвоичныеДанные. Однако этот метод создает лишние операции ввода-вывода, что замедляет работу и "замусоривает" дисковое пространство. Для настройки базового процесса получения данных можно использовать руководство по подключению и получению данных из SQL в 1С.

Рассмотрим, как избавиться от временных файлов и выполнить преобразование `COMSafeArray` в `ДвоичныеДанные` полностью в оперативной памяти.

Проанализируем исходную ситуацию. У нас есть код, который получает данные через ADODB, и для бинарных полей использует временный файл:


Соединение = Новый COMОбъект("ADODB.Connection");
Команда = Новый COMОбъект("ADODB.Command");
Команда.CommandText = "ТекстЗапроса";
НаборЗаписей = Команда.Execute();

ИмяВременногоФайла = ПолучитьИмяВременногоФайла();
Поток = Новый COMОбъект("ADODB.Stream");
Поток.Type = 1; // adTypeBinary
Поток.Mode = 3; // adModeReadWrite
Поток.Open();

Пока НЕ НаборЗаписей.EOF Цикл
    НСтр = ВнешниеДанные.Добавить();
    Для Каждого Колонка Из НаборЗаписей.Fields Цикл
        Значение = НаборЗаписей.Fields(Колонка.Name).Value;
        
        Если ТипЗнч(Значение) = Тип("COMSafeArray") Тогда
            // Проблемный участок кода с использованием временного файла
            Поток.Write(Значение);
            Поток.SaveToFile(ИмяВременногоФайла, 2); // 2 = adSaveCreateOverWrite
            НСтр[Колонка.Name] = Новый ДвоичныеДанные(ИмяВременногоФайла);
            УдалитьФайлы(ИмяВременногоФайла); // Не забываем чистить за собой
        Иначе
            НСтр[Колонка.Name] = Значение;
        КонецЕсли;
    КонецЦикла;
    НаборЗаписей.MoveNext();
КонецЦикла;

Поток.Close();
Соединение.Close();

Основная задача — заменить блок с Поток.Write(), Поток.SaveToFile() и созданием ДвоичныхДанных из файла на более эффективное решение, работающее в памяти.

Почему побайтовый перебор — плохая идея?

Первая мысль, которая может прийти в голову, — это выгрузить COMSafeArray в массив 1С и собрать из него ДвоичныеДанные побайтово. Например, с помощью БуферДвоичныхДанных.

Однако этот подход имеет критический недостаток — низкую производительность. Причина кроется в механизме взаимодействия 1С с COM-объектами (маршалинг). Каждое обращение к элементу массива COMSafeArray из кода 1С — это отдельный вызов COM, который требует переключения контекста выполнения. Если ваш массив содержит сотни тысяч или миллионы байт, это приведет к такому же количеству вызовов, что займет огромное, неприемлемое время. Файл размером 500 КБ может обрабатываться десятки секунд или даже минут.

Поэтому нам нужны методы, которые выполняют преобразование "оптом", передавая весь массив за один вызов.

Решение 1: Преобразование через Base64 и XMLDOM (самый эффективный)

Этот способ является наиболее производительным и универсальным для работы в памяти. Мы используем стандартный COM-объект Microsoft.XMLDOM, который умеет быстро кодировать бинарные данные в строку формата Base64. А 1С, в свою очередь, умеет так же быстро преобразовывать строку Base64 обратно в двоичные данные. При возникновении ошибок инициализации или регистрации необходимых COM-компонентов в Windows полезно использовать инструкцию по устранению проблем с COM-объектами.

Алгоритм действий:

  1. Создаем объект MSXML2.DOMDocument.
  2. Создаем в нем временный XML-элемент.
  3. Устанавливаем для этого элемента тип данных bin.base64.
  4. Передаем наш COMSafeArray в свойство nodeTypedValue элемента. COM-объект сам выполнит всю работу по кодированию.
  5. Забираем готовую строку Base64 из свойства text элемента.
  6. Используем стандартную функцию 1С ПолучитьДвоичныеДанныеИзСтрокиBase64() для финального преобразования.

Посмотрим, как изменится наш код:


// Создаем объекты для конвертации один раз перед циклом
XMLДокумент = Новый COMОбъект("MSXML2.DOMDocument.6.0");
XMLElement = XMLДокумент.CreateElement("root");
XMLElement.dataType = "bin.base64";

// ... код подключения к ADODB ...

Пока НЕ НаборЗаписей.EOF Цикл
    НСтр = ВнешниеДанные.Добавить();
    Для Каждого Колонка Из НаборЗаписей.Fields Цикл
        Значение = НаборЗаписей.Fields(Колонка.Name).Value;
        
        Если ТипЗнч(Значение) = Тип("COMSafeArray") Тогда
            // Эффективное преобразование в памяти
            XMLElement.nodeTypedValue = Значение;
            СтрокаBase64 = XMLElement.text;
            НСтр[Колонка.Name] = ПолучитьДвоичныеДанныеИзСтрокиBase64(СтрокаBase64);
        Иначе
            НСтр[Колонка.Name] = Значение;
        КонецЕсли;
    КонецЦикла;
    НаборЗаписей.MoveNext();
КонецЦикла;

// ... код закрытия соединений ...

Преимущества этого метода:

Решение 2: Оптимизация на стороне источника данных (SQL)

Если у вас есть возможность изменять SQL-запрос, который отправляется в базу данных, то самым лучшим решением будет выполнить преобразование в Base64 прямо на стороне SQL-сервера. В этом случае ADODB вернет вам уже готовую текстовую строку, а не массив байт, что максимально упростит код в 1С.

Пример для MS SQL Server:

В MS SQL Server можно использовать FOR XML PATH для кодирования бинарного поля в Base64.


-- Старый запрос:
-- ВЫБРАТЬ BinaryField FROM MyTable

-- Новый, оптимизированный запрос:
SELECT 
    (SELECT BinaryField AS '*' FOR XML PATH(''), BINARY BASE64) AS BinaryFieldBase64
FROM MyTable

После такого изменения запроса, в 1С вы будете получать обычную строку, и код станет тривиальным:


// ...
Значение = НаборЗаписей.Fields("BinaryFieldBase64").Value;
НСтр[Колонка.Name] = ПолучитьДвоичныеДанныеИзСтрокиBase64(Значение);
// ...

Этот подход является идеальным, так как переносит всю тяжелую работу на сервер баз данных, который для таких операций оптимизирован гораздо лучше, и минимизирует объем передаваемых данных в сложном формате.

Итоги

Для преобразования COMSafeArray в ДвоичныеДанные без использования временных файлов есть два эффективных пути:

  1. Преобразование в Base64 через XMLDOM: Универсальный и быстрый способ, если вы не можете изменить источник данных. Он выполняет всю работу в оперативной памяти с минимальными накладными расходами.
  2. Преобразование на стороне SQL: Самый производительный метод, если у вас есть контроль над SQL-запросом. Он упрощает код 1С и переносит нагрузку на оптимизированный сервер СУБД.

Категорически не рекомендуется использовать побайтовый перебор массива в цикле из-за катастрофического падения производительности на больших объемах данных.

← На главную