Как избежать зависания 1С при рекурсивном поиске файлов на FTP-сервере?

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

При интеграции 1С с внешними сайтами или файловыми хранилищами по протоколу FTP часто возникает задача поиска и выгрузки файлов из вложенных папок. Однако при вызове метода FTPСоединение.НайтиФайлы с включенным флагом поиска во вложенных подкаталогах процесс может наглухо зависнуть. Разберем, почему возникает эта проблема и какими способами можно эффективно обойти большой объем данных на FTP-сервере без блокировки сеанса 1С.

Причины зависания метода НайтиФайлы

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

  1. Синхронная блокирующая рекурсия: Метод FTPСоединение.НайтиФайлы с третьим параметром Истина не возвращает данные порциями. Платформа 1С пытается полностью обойти все дерево каталогов, последовательно отправляя команды серверу, и формирует единый массив объектов FTPФайл в оперативной памяти. Если на сервере тысячи файлов и глубокая структура подпапок, операция занимает огромное количество времени.
  2. Таймауты и разрывы соединений: Если в конструкторе FTPСоединение явно не задан таймаут, при потере пакетов или срабатывании ограничений FTP-сервера (например, защита от частых запросов) сокет переходит в режим бесконечного ожидания ответа.
  3. Символические ссылки (Symlinks): Если на сервере настроены циклические ссылки на родительские каталоги, встроенный рекурсивный механизм 1С может уйти в бесконечный цикл.
  4. Режим передачи (Active/Passive): Неправильно выбранный режим подключения может вызывать зависания при попытке открыть канал передачи данных для чтения списка файлов через фаервол.

Способ 1. Пошаговый нерекурсивный обход через очередь (BFS)

Рассмотрим основной способ решения проблемы: отказ от автоматической рекурсии платформы в пользу контролируемого последовательного обхода каталогов. Мы передаем в метод НайтиФайлы третий параметр Ложь (поиск только в текущем каталоге) и складываем найденные подкаталоги в массив-очередь.

Посмотрим на пример реализации такого алгоритма:


// Инициализируем FTP-соединение с обязательным указанием таймаута и пассивного режима
Таймаут = 30;
ПассивныйРежим = Истина;
ФТПСоединение = Новый FTPСоединение("ftp.example.com", 21, "Логин", "Пароль", , ПассивныйРежим, Таймаут);

КаталогПоиска = "/public_html/images/";
ОчередьКаталогов = Новый Массив;
ОчередьКаталогов.Добавить(КаталогПоиска);

НайденныеФайлы = Новый Массив;
МаскаПоиска = "*.jpg";

// Обходим очередь каталогов
Пока ОчередьКаталогов.Количество() > 0 Цикл
    ТекущийПуть = ОчередьКаталогов[0];
    ОчередьКаталогов.Удалить(0);
    
    // Получаем элементы текущего каталога без рекурсии (параметр Ложь)
    Попытка
        ЭлементыКаталога = ФТПСоединение.НайтиФайлы(ТекущийПуть, "*", Ложь);
    Исключение
        // Логируем ошибку доступа к конкретной папке и продолжаем работу
        ЗаписьЖурналаРегистрации("FTPПоиск", УровеньЖурналаРегистрации.Предупреждение, , , 
            "Ошибка чтения каталога: " + ТекущийПуть + ". Описание: " + ОписаниеОшибки());
        Продолжить;
    КонецПопытки;
    
    Для Каждого Элемент Из ЭлементыКаталога Цикл
        Если Элемент.ЭтоКаталог() Тогда
            // Исключаем ссылки на текущую и родительскую директории
            Если Элемент.Имя <> "." И Элемент.Имя <> ".." Тогда
                ОчередьКаталогов.Добавить(Элемент.ПолноеИмя + "/");
            КонецЕсли;
        Иначе
            // Проверяем соответствие расширению или маске
            Если СтрЗаканчивается(НРег(Элемент.Имя), ".jpg") Или СтрЗаканчивается(НРег(Элемент.Имя), ".jpeg") Тогда
                НайденныеФайлы.Добавить(Элемент.ПолноеИмя);
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
КонецЦикла;

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

Способ 2. Параллельная обработка через фоновые задания

Разберем вариант ускорения работы при наличии очень большого дерева каталогов. Мы можем распределить нагрузку между несколькими фоновыми потоками.

  1. В основном потоке выполняем нерекурсивный поиск только в корневой директории и получаем список папок первого уровня.
  2. Разбиваем полученный список каталогов на группы или отправляем каждую ветку в отдельное фоновое задание через ФоновыеЗадания.Выполнить.
  3. Важно: Объект FTPСоединение не является сериализуемым и не может быть передан в параметры фонового задания. Внутри процедуры фонового задания необходимо инициализировать свой собственный экземпляр FTPСоединение.
  4. Фоновые задания возвращают массивы найденных файлов во временное хранилище или записывают их в регистр сведений/таблицу базы.

Способ 3. Получение готовой карты файлов от сервера

Если есть доступ к веб-серверу, на котором хранятся файлы, наиболее производительным решением будет отказ от сканирования файловой структуры через протокол FTP со стороны 1С:

  1. На стороне сервера размещается легковесный скрипт, сканирующий локальную файловую систему напрямую. Это занимает доли секунды даже для сотен тысяч файлов.
  2. Скрипт формирует структуру каталогов и файлов в формате JSON или сжатом текстовом файле.
  3. 1С загружает готовый файл с картой путей с помощью объекта HTTPСоединение или скачивает его по FTP методом FTPСоединение.Получить, после чего мгновенно разбирает данные встроенными средствами платформы.

Таким образом, для стабильной работы с объемными FTP-хранилищами следует избегать вызова FTPСоединение.НайтиФайлы с флагом рекурсии и использовать пошаговый обход с контролем таймаутов либо выносить индексацию на сторону сервера.

← На главную