В процессе работы с 1С часто возникает задача интеграции со сторонними системами. Одна из таких задач — выгрузка файлов, сформированных в 1С (например, печатных форм отчетов в формате PDF), в базу данных на MS SQL Server. Напрямую передать двоичные данные не всегда очевидно, особенно при работе через COM-объект ADODB.Connection. Рассмотрим один из способов решения этой задачи, его особенности, а также разберем более универсальные и производительные альтернативы.
Проанализируем конкретный пример, который решает задачу для небольших файлов, и выясним, почему он может быть неоптимален для больших объемов данных.
Основная идея этого метода — прочитать файл в двоичные данные, преобразовать их в текстовое представление (шестнадцатеричную строку) и вставить эту строку прямо в текст SQL-запроса. На стороне SQL Server эта строка будет обратно преобразована в двоичный формат и сохранена в поле типа VARBINARY(MAX).
Давайте разберем по шагам, как это реализовать.
ADODB.Connection. Мы настраиваем строку подключения, указывая драйвер, сервер, базу данных и учетные данные.ДвоичныеДанные. Затем, с помощью стандартной функции 1С ПолучитьHexСтрокуИзДвоичныхДанных(), эти данные конвертируются в длинную текстовую строку.INSERT. В эту строку мы подставляем метаданные файла (имя, идентификаторы) и саму HEX-строку. Для того чтобы SQL Server понял, что это шестнадцатеричное представление двоичных данных, мы используем функцию CONVERT(varbinary(max), @hexstring, 2). Третий параметр `2` как раз и указывает на то, что исходная строка не содержит префикса `0x`.Посмотрим на полный код, который реализует этот алгоритм:
// Предполагается, что переменные ИмяСервераSQL, ПользовательSQL,
// ПарольSQL, БазаДанныхSQL, Клиент, ТабличныйДокумент уже определены.
Если ТабличныйДокумент.ВысотаТаблицы = 0 Тогда
// Если отчет пуст, формируем его
СформироватьОтчет("Расш");
КонецЕсли;
/////////////////////////////////////////
// Подключение к SQL-серверу
Соединение = Новый COMОбъект("ADODB.Connection");
Команда = Новый COMОбъект("ADODB.Command");
Выборка = Новый COMОбъект("ADODB.RecordSet");
Соединение.ConnectionString = "driver={SQL Server};" +
"server=" + ИмяСервераSQL + ";" +
"uid=" + ПользовательSQL + ";" +
"pwd=" + ПарольSQL + ";" +
"database=" + БазаДанныхSQL + ";";
Соединение.ConnectionTimeout = 30;
Соединение.CommandTimeout = 600;
// Открытие соединение
Соединение.Open();
Команда.ActiveConnection = Соединение;
// Сохраняем табличный документ во временный файл
ИмяФайла = КаталогВременныхФайлов() + "Отчет.pdf";
ТабличныйДокумент.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.PDF);
Попытка
// Читаем файл в двоичные данные
ДД = Новый ДвоичныеДанные(ИмяФайла);
Исключение
Соединение.Close();
Возврат;
КонецПопытки;
Если ДД.Размер() = 0 Тогда
Соединение.Close();
Возврат;
КонецЕсли;
// Формируем SQL-запрос с HEX-строкой
ТекстКоманды = "
|DECLARE @hexstring varchar(max);
|SET @hexstring = '" + ПолучитьHexСтрокуИзДвоичныхДанных(ДД) + "';
|INSERT INTO ClientFiles
| (UID, CLIENT_UID, NAME, CONTENT)
|VALUES
| ('" + Новый УникальныйИдентификатор() + "', '" + Клиент + "', 'Отчет.pdf', CONVERT(varbinary(max), @hexstring, 2))
|";
Команда.CommandText = ТекстКоманды;
Команда.Execute;
// Закрываем соединение
Соединение.Close();
Несмотря на свою работоспособность, этот метод получил критику со стороны опытных разработчиков. Давайте разберемся, почему, и определим границы его применимости.
Преимущества:
ADODB.Stream, и понятен даже начинающему программисту.Недостатки:
Вывод: данный способ отлично подходит для своей узкой задачи — загрузки очень маленьких файлов. Автор оригинального сообщения указал, что его файлы были размером около 36 КИЛОБАЙТ. Для таких объемов все перечисленные недостатки не являются критичными, и простота кода оправдывает его использование. Если вы уверены, что ваши файлы никогда не превысят нескольких сотен килобайт, это решение может быть для вас приемлемым.
Для более универсальных и надежных решений рекомендуется использовать параметризованные запросы. Это не только безопаснее (защищает от SQL-инъекций), но и позволяет передавать двоичные данные напрямую, без неэффективных преобразований в текст. Для корректной работы с файлами мы будем использовать объект ADODB.Stream.
Алгоритм действий:
Stream.LoadFromFile().Command.CreateParameter() создаем объект параметра. Очень важно указать правильный тип данных — adLongVarBinary (числовой код 205), который предназначен для больших двоичных объектов.Stream.Read().Этот подход является промышленным стандартом для работы с BLOB-объектами (Binary Large Object) и лишен недостатков предыдущего метода. Он эффективно работает с файлами любого размера, ограниченного только возможностями SQL Server (до 2 ГБ для `VARBINARY(MAX)`).
Если вам предстоит работать с файлами огромного размера (десятки и сотни мегабайт), даже второй способ может создать большую нагрузку на сеть и память, так как весь файл читается в память целиком. Для таких случаев в ADO существует механизм порционной загрузки — AppendChunk.
Общая идея:
ADODB.Recordset для целевой таблицы.Recordset.AddNew().Поле.AppendChunk(ПорцияДанных).Recordset.Update().Этот метод самый сложный в реализации, но и самый щадящий по отношению к системным ресурсам. Он гарантирует, что даже при загрузке гигабайтных файлов потребление оперативной памяти будет минимальным.
Рассмотрев три разных подхода, можно сделать следующие выводы:
Выбор конкретного метода всегда зависит от требований вашей задачи, предполагаемого размера файлов и требований к производительности и надежности системы.