При интеграции 1С с внешними системами (например, Service Desk, CRM или ERP сторонних разработчиков) нередко возникает необходимость передавать прикрепленные файлы напрямую в таблицы Microsoft SQL Server. Часто в таких таблицах для хранения файлов используются типы данных image или varbinary(max).
Попытка передать файл через обычную конкатенацию строк в динамическом SQL-запросе приводит к ошибкам синтаксиса либо к тому, что в базу сохраняется не сам бинарный файл, а его строковое представление. Разберем причины возникновения этих ошибок и рассмотрим надежные способы передачи бинарных данных из 1С во внешнюю СУБД.
Проанализируем ситуацию: если мы получаем файл в виде объекта ДвоичныеДанные и пытаемся вставить его в текст запроса через оператор +, платформа 1С неявно преобразует двоичные данные в шестнадцатеричную строку с пробелами между байтами (например, "22 49 44 22 3B...").
Incorrect syntax near ' ').varchar. При сохранении в поле image или varbinary сохраняется именно текст строки, а не исходное содержимое файла, из-за чего сохраненный файл оказывается поврежденным."6 768"), что также ломает SQL-синтаксис при динамической склейке запросов. Для чисел всегда необходимо использовать функцию Формат(Число, "ЧГ=0").Рассмотрим правильные способы передачи двоичных данных.
Наиболее надежный и безопасный способ передачи бинарных данных через COM-соединение — использование параметров команды ADODB.Command совместно с объектом потока ADODB.Stream. Поток позволяет прочитать файл как массив байтов (SafeArray) и передать его в бинарный параметр без строковых преобразований.
Разберем по шагам реализацию этого способа:
// 1. Считываем файл в бинарный поток ADO
Поток = Новый COMОбъект("ADODB.Stream");
Поток.Type = 1; // 1 = adTypeBinary
Поток.Open();
Поток.LoadFromFile(ПутьКФайлу);
// 2. Инициализируем команду ADODB
Команда = Новый COMОбъект("ADODB.Command");
Команда.ActiveConnection = АктивноеСоединение;
Команда.CommandType = 1; // 1 = adCmdText
Команда.CommandText = "INSERT INTO [dbo].[tbl_Files] ([ID], [FileData], [FileSize]) VALUES (?, ?, ?)";
// 3. Создаем и добавляем параметры
// adGUID = 72, adParamInput = 1
ПараметрID = Команда.CreateParameter("ID", 72, 1, 16, Строка(ГУИДФайла));
Команда.Parameters.Append(ПараметрID);
// adLongVarBinary = 205
ПараметрФайл = Команда.CreateParameter("FileData", 205, 1, Поток.Size, Поток.Read());
Команда.Parameters.Append(ПараметрФайл);
// adInteger = 3
ПараметрРазмер = Команда.CreateParameter("FileSize", 3, 1, , Поток.Size);
Команда.Parameters.Append(ПараметрРазмер);
// 4. Выполняем запись в базу
Команда.Execute();
Поток.Close();
Поток = Неопределено;
Команда = Неопределено;
Преимущества: исключаются ошибки форматирования, запрос защищен от SQL-инъекций, а план выполнения запроса кэшируется СУБД.
Если нет необходимости выполнять сложный SQL-запрос, можно использовать объект ADODB.Recordset. Он позволяет добавить новую строку в таблицу напрямую через методы набора записей.
Посмотрим на пример выполнения:
Поток = Новый COMОбъект("ADODB.Stream");
Поток.Type = 1; // adTypeBinary
Поток.Open();
Поток.LoadFromFile(ПутьКФайлу);
НаборЗаписей = Новый COMОбъект("ADODB.Recordset");
// adOpenKeyset = 1, adLockOptimistic = 3
НаборЗаписей.Open("SELECT TOP 0 * FROM [dbo].[tbl_Files]", АктивноеСоединение, 1, 3);
НаборЗаписей.AddNew();
НаборЗаписей.Fields("ID").Value = Строка(ГУИДФайла);
НаборЗаписей.Fields("FileData").Value = Поток.Read();
НаборЗаписей.Fields("FileSize").Value = Поток.Size;
НаборЗаписей.Update();
НаборЗаписей.Close();
Поток.Close();
Если требуется выполнить вставку исключительно текстовой SQL-командой без параметров, двоичные данные необходимо подготовить в виде шестнадцатеричного литерала без одинарных кавычек и с префиксом 0x, предварительно очистив строку от пробелов.
Посмотрим, как это выглядит в коде 1С:
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ПутьКФайлу);
СтрокаHex = СтрЗаменить(Строка(ДвоичныеДанныеФайла), " ", "");
РазмерФайла = Формат(ДвоичныеДанныеФайла.Размер(), "ЧГ=0");
ТекстSQL = "INSERT INTO [dbo].[tbl_Files] ([ID], [FileData], [FileSize]) "
+ "VALUES ('" + Строка(ГУИДФайла) + "', 0x" + СтрокаHex + ", " + РазмерФайла + ")";
Команда = Новый COMОбъект("ADODB.Command");
Команда.ActiveConnection = АктивноеСоединение;
Команда.CommandType = 1;
Команда.CommandText = ТекстSQL;
Команда.Execute();
Важно: этот вариант подходит только для небольших файлов. При передаче файлов большого объема длина строки SQL-запроса может превысить допустимые ограничения сетевого пакета или вызвать избыточную нагрузку на память сервера 1С.
Платформа 1С:Предприятие 8 предоставляет штатный механизм — Внешние источники данных. С его помощью можно связать внешнюю таблицу SQL Server с конфигурацией через ODBC без использования COM-объектов.
tbl_Files.image или varbinary(max) автоматически маппируется в тип ХранилищеЗначения или ДвоичныеДанные.
Менеджер = ВнешниеИсточникиДанных.БазаServiceDesk.Таблицы.tbl_Files.СоздатьМенеджерЗаписи();
Менеджер.ID = Новый УникальныйИдентификатор();
Менеджер.FileData = Новый ДвоичныеДанные(ПутьКФайлу);
Менеджер.FileSize = Новый Файл(ПутьКФайлу).Размер();
Менеджер.Записать();
При настройке обмена обратите внимание на следующие нюансы:
image в Microsoft SQL Server является устаревшим (deprecated). При наличии возможности модернизировать структуру внешней базы рекомендуется использовать тип varbinary(max).INSERT и UPDATE на целевую таблицу базы данных.