Каталог решений - Отказ от работы с временными файлами при работе с двоичными данными или Потоки как простая замена ADODB.Stream и временным файлам

Отказ от работы с временными файлами при работе с двоичными данными или Потоки как простая замена ADODB.Stream и временным файлам

Отказ от работы с временными файлами при работе с двоичными данными или Потоки как простая замена ADODB.Stream и временным файлам

В наличии

В платформе начиная с версии 3.8.9 (как я понял по документации) появился расширенный функционал средств работы с двоичными данными. Если раньше простой и очевидный способ преобразования данных строился на использовании временных файлов, то теперь благодаря новым средствам можно уйти от их использования.

Категория:

Описание

Вместо дисклеймера: Это первая моя статья, поэтому прошу отнестись с пониманием. Цель — попытка показать возможности нового функционала на примере двух конкретных задач.

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

Итак задачи:

  1. Собственная десериализация объектов из механизма версионирования объектов УПП.  Задача — обойтись без временного файла.
  2. Получение данных из SQL базы с varbinary типом данных. Задача — уйти от использования COM объекта ADODB.Stream и временного файла.

Начнем по порядку.

1. Собственная десериализация объектов из механизма версионирования объектов.

Здесь нет абсолютно никаких проблем. Объекты сериализуются стандартными механизмами и помещаются в регистр с ресурсом типа ХранилищеЗначений. И все хорошо за исключением последующей десериализации объектов. Для основы был взят механизм из отчета "История изменения объектов". Опускаем ненужное и идем к самой функции, которая из ресурса с типом ХранилищеЗначений получает строку для десерализации. Всю функцию приводить не буду, нам нужны строки преобразования двоичных данных в строковый тип:

ИмяВременногоФайла = ПолучитьИмяВременногоФайла();
ВерсияОбъекта.Записать(ИмяВременногоФайла);
ТекстовыйДокумент = Новый ТекстовыйДокумент;
ТекстовыйДокумент.Прочитать(ИмяВременногоФайла, КодировкаТекста.UTF8);
СтрокаXML = ТекстовыйДокумент.ПолучитьТекст();
УдалитьФайлы(ИмяВременногоФайла);

И как мы видим — используется механизм временных файлов. ВерсияОбъекта в данном случае имеет тип ДвоичныеДанные, получаемые из ресурса регистра с типом ХранилищеЗначения.

Сразу вопрос — есть ли возможность обойтись без его использования? Проблема заключается в том, что получив двоичные данные мы не можем преобразовать их в строковый тип для десереализации. Однако это не так. Немного покопавшись в великом СП, было найдено решение.

Собственно нас интересует объект "ЧтениеДанных", а точнее конструктор на основе двоичных данных. У этого объекта широйкий набор свойств и методов. И по СП видно, что нам пригодятся чтение данных в строковые типы — а именно два метода ЧтениеСтроки и ЧтениеСимволов. Первый дает возможность читать построчно, но это цикл и нам сейчас ни к чему. Второй читает количество символов, по умолчанию равное всему потоку. Его мы и будем использовать.

В качестве ремарки — оба метода имеют возможность указания вида кодировки в качестве параметра чтения, однако мы укажем кодировку в конструкторе объекта вторым параметром.

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

Читатор = новый ЧтениеДанных(ВерсияОбъекта,КодировкаТекста.UTF8);
СтрокаXML = Читатор.ПрочитатьСимволы();
Читатор.Закрыть(); //не забываем чистить за собой

И в общем то все. Никаких временных файлов, на выходе строка для разбора десериализатором.

В голову приходит логичная мысль — для собственных целей мы получаем объект десереализацией по нашему новому алгоритму без временных файлов, но это не частая операция. А как выглядят дела с механизмом версионирования в принципе? Как происходит запись в регистр? Точнее нас интересует как происходит помещение данных в ХранилищеЗначения. Идем в общий модуль УПП реализующий версионирование и ищем то что нам нужно:

ИмяВременногоФайла = ПолучитьИмяВременногоФайла();

ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ИмяВременногоФайла); 
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписатьXML(ЗаписьXML, Источник, НазначениеТипаXML.Явное);
ЗаписьXML.Закрыть();
		
ДвоичныеДанные = Новый ДвоичныеДанные(ИмяВременногоФайла);
ХранилищеДанных = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));

Бинго! В регистр уходит ХранилищеДанных, но используются временные файлы для формирования ДовичныхДанных. И это понятно конфигурация УПП должна иметь и обратную совместимость. Но у нас уже в руках новые технологии и мы хотим их использовать. В базе с 3000 активными пользователями каждую минуту формируются огромное количество объектов подлежащих версионированию, а значит здесь уже мы имеем возможность практического применения потоков для ускорения и удаления "слабого звена".

Абсолютно справедливо механизм работает и в обратную сторону при помощи объекта ЗаписьДанных. Однако если при чтении  мы уже имеем двоичные данные, которые по сути являются потоком данных и нам нет необходимости его формировать, то при записи нам потребуется поток который мы затем и выгрузим как двоичные данные, а запись в этот поток осуществляется объектом ЗаписьДанных:

ЗаписьXML = новый ЗаписьXML();
ЗаписьXML.УстановитьСтроку();
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписатьXML(ЗаписьXML, Источник, НазначениеТипаXML.Явное);
СтрокаXML = ЗаписьXML.Закрыть();

Поток = новый ПотокВПамяти();
Запись = новый ЗаписьДанных(Поток,КодировкаТекста.UTF8);
Запись.ЗаписатьСимволы(СтрокаXML);
Запись.Закрыть(); //не забываем чистить за собой	
ДвоичныеДанные = Поток.ЗакрытьИПолучитьДвоичныеДанные(); 

ХранилищеДанных = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));

И опять никаких временных файлов. На что стоит обратить внимание — кодировка как и в первом случае может указываться в конструкторе объекта ЗаписьДанных, так и в его методах при непосредственной записи. Кроме того кодировка может указываться и в сериализации, но это уже другая история. Мы снова используем метод записи символов, но можем так же использовать и построчную "порционную" запись.

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

Все выше перечисленное — очевидное использование Потоков. Теперь же сладкое — неочевидное использование потоков, а именно конкретная задача по чтению данных из SQL.

2. Получение данных из SQL базы с varbinary типом данных.

Возникла задача — создать обработку, которая будет обращаться к внешней базе данных на MS SQL и хранить в ней макеты текущих версий разрабатываемых внешних обработок. Такой некий совсем простой аналог GIT. Это обработки начального заполнения данных из разных источников, а необходимость хранения во внешнем источнике так как в процессе разработки параллельно тестируется около 20 баз и потребовался единый источник обработок заполнения. Возможно подход и неправильный, но речь сейчас не об этом.

В ходе работы с ADODB возникла проблема получения двоичных данных из varbinary типа SQL. Все кто работал и сталкивался понимают о чем идет речь — при получении значения из базы через ADODB.Recordset нам вываливается значение типа COMSafeArray с целочисленными элементами.

Не надо быть гением чтобы понять, что нам вываливают байты в десятиричном исчислении. Просто попытавшись перевести их в 16-тиричный формат это становится очевидным, а сам массив и есть наша последовательность байтов — по сути поток двоичных данных. Все решения найденные в "этих ваших интернетах" сводятся к одному — использовать ADODB.Stream, в котором как раз таки есть функция преобразования такого потока данных в файл, который нам и предлагают затем читать. Полное описание и способы работы не буду приводить, так как не в этом цель. Главное что следует понять ADODB.Stream есть ничто иное как обертка работы с потоками, а если у нас теперь есть возможность работать с потоками в 1С, значит все что делает эта обертка, мы можем сделать штатными средствами.

Начинаем копать и думать. Первая мысль — преобразовать каждый элемент в 16-тиричный формат и попытаться сформировать двоичные данные по символьным значениям. Но через 30 секунд отметаем идею — она тупиковая так как налицо избыточность используемых объемов памяти, и "детскость" в подходе. Вывод — смотрим в сторону потоков в 1С. Мы понимаем что наш COMSafeArray  это и есть поток данных, но в "особом" виде. Курим СП выискивая возможность формирования потока в 1С на основе нашего. Основной объект который мы смотрим ПотокВПамяти. Понятно что нам надо формировать его, однако не очевиден способ.

Вертим объект Поток со всех сторон и не находим методов нас удовлетворяющих. Однако находим зацепку — поток данных можно сформировать конструктором на основе буфера. Курим объект БуферДвоичныхДан

ных в СП и выясняем что БуферДвоичныхДанных согласно СП это:

Описание:
Коллекция байтов фиксированного размера с возможностью произвольного доступа и изменения по месту.

has been added to your cart:
Оформление заказа