При работе в клиент-серверном варианте 1С:Предприятие начинающие (и не только) разработчики часто сталкиваются с ошибкой "Файл не обнаружен" при попытке обработать файл внутри фонового задания. Ситуация кажется парадоксальной: код создания файла отработал без ошибок, файл физически существует на диске, но ФоновоеЗадание упорно его "не видит".
В этой статье мы подробно разберем, почему возникает эта проблема, как контексты исполнения влияют на доступ к файловой системе и какие архитектурные подходы позволяют избежать подобных ошибок при распараллеливании процессов.
Рассмотрим стандартную ситуацию: пользователь работает в Толстом клиенте (обычное приложение). Процедура выгрузки запускается в контексте клиента. Она создает временный файл в каталоге, который возвращает функция КаталогВременныхФайлов(). На локальной машине пользователя это может быть путь вида C:\Users\ИмяПользователя\AppData\Local\Temp\....
Затем инициируется фоновое задание через метод ФоновыеЗадания.Выполнить(). Важно помнить: фоновое задание всегда выполняется на сервере 1С под учетной записью, от имени которой запущена служба сервера (например, USR1CV8).
Проанализируем, почему сервер не находит файл:
C:\Temp для них означает совершенно разные физические папки на разных жестких дисках.КаталогВременныхФайлов() для пользователя и для службы сервера 1С — это разные директории, защищенные правами доступа операционной системы.Посмотрим на пример кода, который вызывает ошибку. Здесь автор пытается передать путь к файлу в фоновое задание:
Процедура ВыгрузитьОбъектВXML(Объект, ИмяФайла, МассивПапок) Экспорт
ПолноеИмяФайла = КаталогВременныхФайлов() + ИмяФайла;
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ПолноеИмяФайла);
// ... запись объекта в XML ...
ЗаписьXML.Закрыть();
МассивПараметров = Новый Массив;
МассивПараметров.Добавить(ПапкаВыгрузки);
МассивПараметров.Добавить(ИмяФайла);
МассивПараметров.Добавить(ПолноеИмяФайла); // Передаем путь
// Фоновое задание не найдет этот файл!
Задание = ФоновыеЗадания.Выполнить("МодульТест.СкопироватьФайлВКаталог", МассивПараметров);
КонецПроцедуры
Ошибка "Файл не обнаружен" здесь закономерна, так как процедура СкопироватьФайлВКаталог, исполняясь на сервере, пытается обратиться к локальному диску клиента.
Если объем передаваемых данных невелик (например, один объект номенклатуры или контрагента), самым простым и надежным решением будет отказаться от промежуточного файла. Вместо этого мы можем сериализовать объект в XML-строку и передать эту строку непосредственно в параметрах фонового задания.
Разберем обновленный алгоритм:
ФоновыеЗадания.Выполнить().Посмотрим на реализацию этого подхода:
Процедура ВыгрузитьОбъектВXML(Объект, ИмяФайла, МассивПапок) Экспорт
// Сериализуем объект в строку вместо файла
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
СериализаторXDTO.ЗаписатьXML(ЗаписьXML, Объект, НазначениеТипаXML.Явное);
ОбъектСтрокой = ЗаписьXML.Закрыть();
Для каждого ПапкаВыгрузки Из МассивПапок Цикл
МассивПараметров = Новый Массив;
МассивПараметров.Добавить(ПапкаВыгрузки);
МассивПараметров.Добавить(ИмяФайла);
МассивПараметров.Добавить(ОбъектСтрокой); // Передаем данные строкой
ФоновыеЗадания.Выполнить("МодульТест.СоздатьФайлНаСервере", МассивПараметров);
КонецЦикла;
КонецПроцедуры
// Процедура, выполняемая в фоновом задании на сервере
Процедура СоздатьФайлНаСервере(ПапкаВыгрузки, ИмяФайла, ОбъектСтрокой) Экспорт
// На сервере путь к папке должен быть доступен службе 1С (UNC-путь или локальный путь сервера)
Текст = Новый ЗаписьТекста(ПапкаВыгрузки + "\" + ИмяФайла, КодировкаТекста.UTF8);
Текст.Записать(ОбъектСтрокой);
Текст.Закрыть();
КонецПроцедуры
Если объект большой и передача его через строку параметров замедляет систему, следует использовать Временное хранилище. Это "мостик" между клиентом и сервером.
Алгоритм действий следующий:
Адрес = ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор).Адрес в фоновое задание.Данные = ПолучитьИзВременногоХранилища(Адрес).Важный нюанс: Чтобы данные во временном хранилище были доступны фоновому заданию, при вызове ПоместитьВоВременноеХранилище нужно передать УникальныйИдентификатор именно того фонового задания, которое будет их использовать, либо использовать идентификатор формы (если вызов с клиента).
Многие разработчики жалуются, что точка останова в процедуре фонового задания не срабатывает. Выясним, как настроить отладку правильно:
-debug. Без этого отладка на сервере невозможна.Проанализируем ситуацию, когда фоновое задание запускается в модуле объекта внутри события ПриЗаписи. Это опасный момент. Вся запись объекта происходит внутри транзакции базы данных.
Помните: фоновое задание, запущенное внутри транзакции, не начнет выполняться реально, пока транзакция основного сеанса не будет успешно завершена (зафиксирована). Если в коде после запуска фонового задания произойдет ошибка и транзакция откатится, фоновое задание будет отменено системой. Если же фоновому заданию нужно прочитать данные того же объекта из базы, оно может увидеть их старое состояние, если транзакция еще не "отпустила" блокировки.
Чтобы ваш код был универсальным и не зависел от операционной системы, придерживайтесь следующих правил:
ПолучитьРазделительПути() вместо жестко прописанных слешей \ или /. Это обеспечит работу кода как на Windows, так и на Linux серверах.\\ServerName\ShareFolder\file.xml), а не подключенные сетевые диски (типа Z:\), так как сетевые диски индивидуальны для каждого пользователя и не видны службе сервера.USR1CV8 (или другого, под которым работает сервер) на целевые сетевые папки.Выяснив причины возникновения ошибок доступа в фоновых заданиях, мы приходим к выводу, что наиболее стабильный способ передачи данных — это абстрагирование от файловой системы клиента и использование механизмов платформы (XDTO, временное хранилище) для передачи информации напрямую в серверный контекст.