Почему пропадает файл при передаче в фоновое задание 1С и как это исправить?

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

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

Рассмотрим эту ситуацию подробнее, выясним причину такого поведения системы и разберем по шагам рабочие способы решения данной задачи.

Проанализируем проблемный подход

Очень часто программисты пишут примерно следующий код для запуска обработки в фоне:


&НаСервере
Процедура ЗагрузитьДанныеНаСервере(АдресВХранилище, РасширениеФайла)
    ФайлВременногоХранилища = ПолучитьИзВременногоХранилища(АдресВХранилище);
    ИмяФайла = ПолучитьИмяВременногоФайла(РасширениеФайла);
    ФайлВременногоХранилища.Записать(ИмяФайла);
    
    ПараметрыФоновогоЗадания = Новый Массив;    
    ПараметрыФоновогоЗадания.Добавить(ИмяФайла); // Передаем путь к файлу на диске
    
    // Файл существует до запуска фона
    ВыбФайл = Новый Файл(ИмяФайла);        
    Сущ = ВыбФайл.Существует(); // Возвращает Истина
    
    ФоновыеЗадания.Выполнить("МодульЗагрузки.ОбработатьФайл", ПараметрыФоновогоЗадания);
КонецПроцедуры

А внутри процедуры фонового задания мы получаем ошибку. Метод Существует() возвращает Ложь. Куда пропадает файл?

Почему фоновое задание не видит файл?

Разберем основные причины, из-за которых возникает эта ошибка:

  1. Работа в кластере серверов. Это самая частая причина. В крупных компаниях кластер 1С состоит из нескольких рабочих серверов. Когда мы вызываем метод ПолучитьИмяВременногоФайла, система создает файл в локальной папке Temp того физического сервера, где обрабатывается текущий вызов. Но когда мы запускаем метод ФоновыеЗадания.Выполнить, менеджер кластера может решить запустить это задание на другом рабочем сервере для балансировки нагрузки. Естественно, на жестком диске второго сервера по указанному пути файла не существует.
  2. Изоляция сеансов. Фоновое задание — это абсолютно отдельный сеанс в системе. У него свой контекст. Временное хранилище, созданное в родительском сеансе без привязки к конкретному идентификатору, будет недоступно в фоновом задании.
  3. Права доступа в ОС. Иногда разные рабочие процессы 1С (rphost) могут быть запущены от имени разных пользователей операционной системы, из-за чего возникают проблемы с правами на чтение из чужих временных каталогов.

Решение 1: Передача через ДвоичныеДанные (самый надежный способ)

Посмотрим на пример решения, которое устраняет проблему с разными серверами в кластере. Параметры, которые мы передаем в фоновое задание, сериализуются платформой и передаются в новый сеанс независимо от того, на каком узле он стартовал. Этим можно воспользоваться, передав сам контент файла, а не путь к нему.

Вместо передачи строкового пути к файлу, передадим объект ДвоичныеДанные:


&НаСервере
Процедура ЗагрузитьДанныеНаСервере(АдресВХранилище)
    // Получаем двоичные данные файла напрямую из хранилища
    ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(АдресВХранилище);
    
    ПараметрыФоновогоЗадания = Новый Массив;    
    ПараметрыФоновогоЗадания.Добавить(ДвоичныеДанныеФайла); // Передаем содержимое
    
    ФоновыеЗадания.Выполнить("МодульЗагрузки.ОбработатьФайл", ПараметрыФоновогоЗадания);
КонецПроцедуры

Теперь в самом фоновом задании мы принимаем эти данные, сохраняем их во временный файл уже в контексте того сервера, где запустился фон, и спокойно работаем:


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

Это решение гарантирует доставку файла в память процесса. Разобраться в других особенностях клиент-серверной передачи данных поможет руководство по работе с файлами в управляемых формах 8.3.

Решение 2: Использование ВременногоХранилища с уникальным идентификатором

Если файл слишком большой, и вы боитесь передавать его напрямую в массиве параметров, можно использовать ВременноеХранилище. Но делать это нужно правильно.

Чтобы данные из хранилища не удалились и были доступны в фоновом задании, нужно поместить их туда с привязкой к УникальныйИдентификатор, а сам этот идентификатор (или адрес хранилища) передать в фон:


&НаСервере
Процедура ЗагрузитьДанныеНаСервере()
    // ... получение данных ...
    УИД_Задания = Новый УникальныйИдентификатор;
    
    // Помещаем данные с привязкой к конкретному УИД
    АдресДанных = ПоместитьВоВременноеХранилище(МоиДвоичныеДанные, УИД_Задания);
    
    ПараметрыФоновогоЗадания = Новый Массив;    
    ПараметрыФоновогоЗадания.Добавить(АдресДанных);
    
    // Запускаем задание с тем же УИД!
    ФоновыеЗадания.Выполнить("МодульЗагрузки.ОбработатьФайл", ПараметрыФоновогоЗадания, УИД_Задания);
КонецПроцедуры

В фоновом задании мы вызываем ПолучитьИзВременногоХранилища по переданному адресу и получаем наши данные. Для правильной организации длительных операций и получения их результатов без БСП можно использовать пример реализации фоновых заданий с отслеживанием прогресса.

Решение 3: Настройка единого каталога временных файлов кластера

Рассмотрим административный метод решения проблемы. Если вы не можете переписать код конфигурации, можно заставить все серверы кластера использовать единую общую папку для временных файлов.

В свойствах кластера 1С (через консоль администрирования) есть настройка "Каталог временных файлов кластера серверов". Если вы укажете там общий сетевой UNC-путь (например, \\shared-server\1c_temp), доступный всем рабочим серверам с правами на чтение и запись, то метод ПолучитьИмяВременногоФайла() всегда будет возвращать путь именно в этой сетевой папке. В таком случае файл, сохраненный одним сервером, всегда будет найден фоновым заданием на другом сервере.

← На главную