Как сохранить файл на сервере базы-приемника при использовании COM-соединения

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При интеграции двух информационных баз 1С через COMConnector разработчики часто сталкиваются с неочевидной проблемой: файл, который должен быть записан на удаленном сервере (базе-приемнике), почему-то оказывается на диске сервера-источника. В этой статье мы подробно разберем, почему так происходит, изучим механизмы контекстов исполнения и реализуем корректную схему передачи данных.

Анализ проблемы: почему файл сохраняется не там?

Рассмотрим типовую ситуацию. Программист создает объект V83.COMConnector, подключается к удаленной базе и пытается вызвать метод Записать() для объекта ДвоичныеДанные. На первый взгляд кажется, что раз объект получен из «внешней базы», то и команды его методов должны выполняться там. Однако это заблуждение.

Когда мы работаем через COM, создается сессия с типом Внешнее соединение. Код, который мы пишем в базе-источнике, обращаясь к объектам ВнешняяБаза, фактически выполняется в потоке клиентского процесса (или серверного процесса базы-источника), который инициировал соединение. Система интерпретирует путь к файлу относительно того компьютера, на котором запущен сам код управления COM-объектом.

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


// Код в базе-источнике
COM1с = Новый COMObject("V83.COMConnector");
ВнешняяБаза = COM1с.Connect(СтрокаСоединения);
ДвоичныеДанныеНаСервере = ВнешняяБаза.NewObject("ДвоичныеДанные", ПутьКФайлуИсточнику);
// Файл будет записан на сервере ИСТОЧНИКЕ, а не ПРИЕМНИКЕ
ДвоичныеДанныеНаСервере.Записать("C:\Exchange\file.xml"); 

Чтобы решить эту задачу, нам необходимо принудительно перенести выполнение кода из контекста внешнего соединения в контекст сервера приложений базы-приемника.

Решение проблемы: прыжок в серверный контекст

Для того чтобы команда записи выполнилась физически на дисках удаленного сервера, нам нужно создать специальную «прослойку» в базе-приемнике. Разберем по шагам, как это реализовать.

Шаг 1. Настройка общих модулей в базе-приемнике

Нам потребуется два общих модуля (или один с комбинированными свойствами, но лучше разделить логику). Главное условие — наличие флага Вызов сервера. Без этого флага выполнение не покинет пределы потока внешнего соединения.

  1. Создадим общий модуль хаз_ОбменКонтрагентами. Установим флаги: Сервер, Внешнее соединение.
  2. Создадим общий модуль хаз_ОбменКонтрагентамиВызовСервера. Установим флаги: Сервер, Вызов сервера.

Шаг 2. Реализация кода в базе-приемнике

В модуле хаз_ОбменКонтрагентами пропишем экспортную процедуру, которую будем вызывать извне. Она будет служить входной точкой:


// Общий модуль: хаз_ОбменКонтрагентами (Сервер, Внешнее соединение)
Процедура СохранитьФайлНаСервере(ДанныеФайлаВВидеДвоичныхДанных, ИмяФайлаПриемника) Экспорт
    // Здесь мы все еще в контексте внешнего соединения.
    // Переходим в истинный серверный контекст через модуль с "Вызовом сервера"
    хаз_ОбменКонтрагентамиВызовСервера.ЗаписатьДанныеНаДиск(ДанныеФайлаВВидеДвоичныхДанных, ИмяФайлаПриемника);
КонецПроцедуры

В модуле хаз_ОбменКонтрагентамиВызовСервера реализуем непосредственную запись:


// Общий модуль: хаз_ОбменКонтрагентамиВызовСервера (Сервер, Вызов сервера)
Процедура ЗаписатьДанныеНаДиск(ДвоичныеДанные, ПолныйПуть) Экспорт
    // Выполнение этой строки произойдет строго на сервере приложений базы-приемника
    ДвоичныеДанные.Записать(ПолныйПуть);
КонецПроцедуры

Шаг 3. Вызов из базы-источника

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


// Код в базе-источнике
ДвоичныеДанныеДляОтправки = Новый ДвоичныеДанные(ПутьКЛокальномуФайлу);
// Вызываем метод удаленной базы
ВнешняяБаза.хаз_ОбменКонтрагентами.СохранитьФайлНаСервере(ДвоичныеДанныеДляОтправки, "D:\Data\In\Message.xml");

Важные нюансы работы с временными файлами

Рассмотрим ситуацию с функцией ПолучитьИмяВременногоФайла(). Выясним причину путаницы: если вы вызываете ВнешняяБаза.ПолучитьИмяВременногоФайла(), платформа вернет путь, который является временным с точки зрения вашего текущего сеанса (источника), но сформирован по правилам удаленной системы. Это не гарантирует, что запись по этому пути «пробросится» на удаленный сервер.

Проанализируем эксперимент из обсуждения: Метод Записать() всегда производит запись в контексте процесса, владеющего объектом. Если объект ДвоичныеДанные живет в памяти процесса COMConnector на стороне сервера «А», он будет пытаться найти путь C:\Temp именно на сервере «А», даже если путь был получен от сервера «Б».

Единственный надежный способ — передача объекта данных внутрь процедуры, которая помечена как Сервер и Вызов сервера в целевой базе.

Безопасность и права доступа

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

Альтернативный подход: Временное хранилище

Если файлы имеют значительный объем (сотни мегабайт), передача через параметры COM-методов может быть нестабильной. В таких случаях рассмотрим алгоритм через временное хранилище:

  1. В базе-источнике читаем файл в ДвоичныеДанные.
  2. Передаем эти данные в функцию базы-приемника.
  3. Внутри базы-приемника используем ПоместитьВоВременноеХранилище(Данные).
  4. Запускаем ФоновыеЗадания.Выполнить(), передавая адрес хранилища.

Фоновое задание в 1С всегда выполняется в контексте сервера, что гарантирует сохранение файла в файловой системе сервера приложений без каких-либо исключений, связанных с типом соединения.

Использование описанных выше методик позволит вам четко контролировать местоположение файлов и избежать типичных ошибок, связанных с контекстной зависимостью платформы 1С:Предприятие при работе через COM-интерфейс.

← На главную