Как быстро прочитать состав каталога и всех вложенных папок в 1С

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

При работе с файловыми хранилищами, архивами или логами программисты 1С часто сталкиваются с задачей рекурсивного обхода каталогов. Когда счет файлов идет на сотни тысяч или миллионы, стандартные средства платформы начинают работать неприемлемо медленно. Разберем, почему это происходит, и проанализируем наиболее эффективные способы решения этой задачи, начиная от простых скриптов и заканчивая низкоуровневыми системными вызовами.

Проблема стандартного метода НайтиФайлы

Рассмотрим классический «лобовой» подход, который использует большинство разработчиков. Он заключается в рекурсивном вызове встроенной функции НайтиФайлы(). Проанализируем типичный пример кода:


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

Почему это медленно? Основная потеря времени происходит не на самом поиске, а на обращении к свойствам объекта Файл. При каждом вызове методов Размер() или ПолучитьВремяИзменения() платформа 1С выполняет отдельный системный запрос к файловой таблице (MFT). Если файлов миллион, мы получаем миллионы микро-запросов к диску, что создает колоссальные накладные расходы.

Использование COM-объекта FileSystemObject (FSO)

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

Рассмотрим реализацию обхода через FSO:


Процедура ПолучитьИнформациюОФайлах(Путь)
    ТабФайлов.Очистить();
    ФСО = Новый COMОбъект("Scripting.FileSystemObject");
    ОбходПодпапок(ФСО.GetFolder(Путь));
КонецПроцедуры

Процедура ОбходПодпапок(Folder)
    // Обрабатываем файлы в текущей папке
    Для Каждого файл Из Folder.Files Цикл
        Стр = ТабФайлов.Добавить();
        Стр.ДатаИзменения = файл.DateLastModified;
        Стр.Имя = файл.Name;
        Стр.Путь = файл.Path;
        Стр.Размер = файл.Size;
    КонецЦикла;
    
    // Рекурсивно заходим в подпапки
    Для Каждого Subfolder Из Folder.SubFolders Цикл
        ОбходПодпапок(Subfolder);
    КонецЦикла;
КонецПроцедуры

По результатам тестов, для структуры из 660 000 файлов такой метод может занять около 12 минут. Это быстрее стандартного метода 1С, но все еще недостаточно эффективно для критически важных задач.

Самый быстрый способ: Использование системной команды DIR

Выясним причину, по которой системные утилиты работают быстрее. Утилита dir оптимизирована для прямого чтения потока данных из файловой системы. Мы можем запустить формирование текстового отчета средствами Windows и затем прочитать его в 1С. Это избавляет нас от создания сотен тысяч промежуточных объектов в памяти 1С.

Разберем алгоритм реализации:

  1. Формируем командную строку для запуска cmd.exe.
  2. Используем флаг /S для рекурсивного поиска и /T:W для вывода времени записи.
  3. Перенаправляем вывод в текстовый файл с помощью оператора >.
  4. Читаем полученный файл в 1С.

ИмяВременногоФайла = КаталогВременныхФайлов() + "file_list.txt";
Команда = "cmd /c dir """ + ПутьККаталогу + """ /S /R > """ + ИмяВременногоФайла + """";
КомандаСистемы(Команда);

// Далее используем ТекстовыйДокумент или ЧтениеТекста для парсинга файла
Текст = Новый ТекстовыйДокумент;
Текст.Прочитать(ИмяВременногоФайла, КодировкаТекста.OEM);

Этот метод позволяет обработать 1 200 000 файлов примерно за 10 минут. Основное преимущество здесь в том, что операционная система сама кэширует чтение файловой таблицы, а 1С получает уже готовый текстовый массив данных.

Альтернатива: Использование Robocopy

Если требуется еще большая надежность и скорость (особенно на сетевых ресурсах), проанализируем использование утилиты Robocopy. Она встроена в современные версии Windows и обладает мощными механизмами многопоточного сканирования.

Для получения списка файлов без их копирования используется флаг /L. Пример команды:

robocopy "C:\Source" "C:\FakeDest" /L /S /FP /NJH /NJS /TS /NC /NS

Разберем ключи:

Использование .NET и WinAPI (для продвинутых задач)

Если ваша конфигурация работает под управлением Windows и вам доступно использование объектов System.IO, мы можем применить методы .NET Framework. Например, класс DirectoryInfo и метод EnumerateFiles работают значительно эффективнее, чем GetFiles, так как начинают возвращать данные сразу, не дожидаясь окончания сканирования всего дерева.

Для достижения абсолютного максимума скорости следует смотреть в сторону внешних компонент на C++, использующих функции WinAPI FindFirstFileW и FindNextFileW. Эти функции позволяют считать структуру WIN32_FIND_DATA, которая содержит имя, размер и даты файла за один единственный вызов. Это исключает лишние обращения к MFT, о которых мы говорили в начале.

Общие рекомендации по оптимизации

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

  1. Минимизируйте обращения к свойствам: Если используете НайтиФайлы(), старайтесь не вызывать методы объекта Файл внутри цикла без крайней необходимости.
  2. Исключения антивируса: Процесс сканирования может быть замедлен антивирусом в десятки раз. Добавьте процесс 1cv8.exe или сканируемую папку в исключения.
  3. Чтение логов: Парсинг текстового файла, созданного через dir или robocopy, почти всегда быстрее, чем прямой обход объектов через COM или встроенные функции 1С на больших объемах.
  4. Кэширование: Если данные о файлах нужны часто, рассмотрите возможность хранения индекса (пути и даты изменения) в справочнике или регистре сведений 1С, обновляя только изменившиеся папки.

Таким образом, для большинства повседневных задач оптимальным по соотношению «сложность/скорость» является метод с использованием системной команды dir и последующим чтением текстового файла. Если же требуется работа в промышленном масштабе с миллионами объектов, стоит рассмотреть написание внешней компоненты на Native API.

← На главную