Получить все файлы и каталоги по определенному пути быстро

Я создаю приложение для резервного копирования, где С# сканирует каталог. Прежде чем использовать это, чтобы получить все файлы и подфайлы в каталоге:

DirectoryInfo di = new DirectoryInfo("A:\\");
var directories= di.GetFiles("*", SearchOption.AllDirectories);

foreach (FileInfo d in directories)
{
       //Add files to a list so that later they can be compared to see if each file
       // needs to be copid or not
}

Единственная проблема заключается в том, что иногда к файлу не удалось получить доступ, и я получаю несколько ошибок. пример ошибки, которую я получаю: error

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

try
{
    files = di.GetFiles(searchPattern, SearchOption.TopDirectoryOnly);               
}
catch
{
     //info of this folder was not able to get
     lstFilesErrors.Add(sDir(di));
     return;
}

Таким образом, этот метод отлично работает, единственная проблема заключается в том, что при сканировании большого каталога это занимает много времени. Как я мог ускорить этот процесс? Мой фактический метод - это, если вам это нужно.

private void startScan(DirectoryInfo di)
{
    //lstFilesErrors is a list of MyFile objects
    // I created that class because I wanted to store more specific information
    // about a file such as its comparePath name and other properties that I need 
    // in order to compare it with another list

    // lstFiles is a list of MyFile objects that store all the files
    // that are contained in path that I want to scan

    FileInfo[] files = null;
    DirectoryInfo[] directories = null;
    string searchPattern = "*.*";

    try
    {
        files = di.GetFiles(searchPattern, SearchOption.TopDirectoryOnly);               
    }
    catch
    {
        //info of this folder was not able to get
        lstFilesErrors.Add(sDir(di));
        return;
    }

    // if there are files in the directory then add those files to the list
    if (files != null)
    {
        foreach (FileInfo f in files)
        {
            lstFiles.Add(sFile(f));
        }
    }


    try
    {
        directories = di.GetDirectories(searchPattern, SearchOption.TopDirectoryOnly);
    }
    catch
    {
        lstFilesErrors.Add(sDir(di));
        return;
    }

    // if that directory has more directories then add them to the list then 
    // execute this function
    if (directories != null)
        foreach (DirectoryInfo d in directories)
        {
            FileInfo[] subFiles = null;
            DirectoryInfo[] subDir = null;

            bool isThereAnError = false;

            try
            {
                subFiles = d.GetFiles();
                subDir = d.GetDirectories();

            }
            catch
            {
                isThereAnError = true;                                                
            }

            if (isThereAnError)
                lstFilesErrors.Add(sDir(d));
            else
            {
                lstFiles.Add(sDir(d));
                startScan(d);
            }


        }

}

Ant проблема, если я попытаюсь обработать исключение с чем-то вроде:

DirectoryInfo di = new DirectoryInfo("A:\\");
FileInfo[] directories = null;
            try
            {
                directories = di.GetFiles("*", SearchOption.AllDirectories);

            }
            catch (UnauthorizedAccessException e)
            {
                Console.WriteLine("There was an error with UnauthorizedAccessException");
            }
            catch
            {
                Console.WriteLine("There was antother error");
            }

Это, если возникает исключение, я не получаю никаких файлов.

Ответ 1

Этот метод выполняется намного быстрее. Вы можете работать только при размещении большого количества файлов в каталоге. Мой внешний жесткий диск A:\содержит почти 1 терабит, поэтому он имеет большое значение при работе с большим количеством файлов.

static void Main(string[] args)
{
    DirectoryInfo di = new DirectoryInfo("A:\\");
    FullDirList(di, "*");
    Console.WriteLine("Done");
    Console.Read();
}

static List<FileInfo> files = new List<FileInfo>();  // List that will hold the files and subfiles in path
static List<DirectoryInfo> folders = new List<DirectoryInfo>(); // List that hold direcotries that cannot be accessed
static void FullDirList(DirectoryInfo dir, string searchPattern)
{
    // Console.WriteLine("Directory {0}", dir.FullName);
    // list the files
    try
    {
        foreach (FileInfo f in dir.GetFiles(searchPattern))
        {
            //Console.WriteLine("File {0}", f.FullName);
            files.Add(f);                    
        }
    }
    catch
    {
        Console.WriteLine("Directory {0}  \n could not be accessed!!!!", dir.FullName);                
        return;  // We alredy got an error trying to access dir so dont try to access it again
    }

    // process each directory
    // If I have been able to see the files in the directory I should also be able 
    // to look at its directories so I dont think I should place this in a try catch block
    foreach (DirectoryInfo d in dir.GetDirectories())
    {
        folders.Add(d);
        FullDirList(d, searchPattern);                    
    }

}

Кстати, я получил это благодаря вашему комментарию Джим Мишель

Ответ 2

В .NET 4.0 существует Directory.EnumerateFiles метод, который возвращает IEnumerable<string> и не загружает все файлы в памяти. Это только после того, как вы начнете перебирать возвращаемую коллекцию, и файлы будут возвращены, и можно было бы обработать исключения.

Ответ 3

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

Лучшее, что мне удалось сделать, было завершено в моей библиотеке и показано как FindFile (source) в пространстве имен CSharpTest.Net.IO. Этот класс может перечислять файлы и папки без ненужных распределений GC и сортировки строк.

Использование достаточно простое, и свойство RaiseOnAccessDenied будет пропускать каталоги и файлы, к которым у пользователя нет доступа:

    private static long SizeOf(string directory)
    {
        var fcounter = new CSharpTest.Net.IO.FindFile(directory, "*", true, true, true);
        fcounter.RaiseOnAccessDenied = false;

        long size = 0, total = 0;
        fcounter.FileFound +=
            (o, e) =>
            {
                if (!e.IsDirectory)
                {
                    Interlocked.Increment(ref total);
                    size += e.Length;
                }
            };

        Stopwatch sw = Stopwatch.StartNew();
        fcounter.Find();
        Console.WriteLine("Enumerated {0:n0} files totaling {1:n0} bytes in {2:n3} seconds.",
                          total, size, sw.Elapsed.TotalSeconds);
        return size;
    }

Для моего локального диска C:\это выводит следующее:

Перечислил 810 046 файлов на общую сумму 307,707,792,662 байт за 232,876 секунды.

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

Ответ 4

Вы можете использовать это, чтобы получить все каталоги и подкаталоги. Затем просто выполните цикл для обработки файлов.

string[] folders = System.IO.Directory.GetDirectories(@"C:\My Sample Path\","*", System.IO.SearchOption.AllDirectories);

foreach(string f in folders)
{
   //call some function to get all files in folder
}

Ответ 5

(скопировал этот фрагмент из моего другого ответа в другом вопросе)

Показать прогресс при поиске всех файлов в каталоге

Перечисление быстрых файлов

Конечно, как вы уже знаете, есть много способов сделать перечисление самостоятельно... но никто не будет мгновенным. Вы можете попробовать использовать USN Journal файловой системы для сканирования. Взгляните на этот проект в CodePlex: MFT Scanner в VB.NET... он нашел все файлы в моей IDE SATA (не SSD ) диск менее чем за 15 секунд и нашел 311000 файлов.

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

Ответ 6

Я знаю, что это старый, но... Другой вариант может заключаться в использовании FileSystemWatcher:

void SomeMethod()
{
    System.IO.FileSystemWatcher m_Watcher = new System.IO.FileSystemWatcher();
    m_Watcher.Path = path;
    m_Watcher.Filter = "*.*";
    m_Watcher.NotifyFilter = m_Watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
    m_Watcher.Created += new FileSystemEventHandler(OnChanged);
    m_Watcher.EnableRaisingEvents = true;
}

private void OnChanged(object sender, FileSystemEventArgs e)
    {
        string path = e.FullPath;

        lock (listLock)
        {
            pathsToUpload.Add(path);
        }
    }

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

Ответ 7

Возможно, это будет полезно для вас. Вы можете использовать метод DirectoryInfo.EnumerateFiles и обрабатывать UnauthorizedAccessException по мере необходимости.

using System;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        DirectoryInfo diTop = new DirectoryInfo(@"d:\");
        try
        {
            foreach (var fi in diTop.EnumerateFiles())
            {
                try
                {
                    // Display each file over 10 MB; 
                    if (fi.Length > 10000000)
                    {
                        Console.WriteLine("{0}\t\t{1}", fi.FullName, fi.Length.ToString("N0"));
                    }
                }
                catch (UnauthorizedAccessException UnAuthTop)
                {
                    Console.WriteLine("{0}", UnAuthTop.Message);
                }
            }

            foreach (var di in diTop.EnumerateDirectories("*"))
            {
                try
                {
                    foreach (var fi in di.EnumerateFiles("*", SearchOption.AllDirectories))
                    {
                        try
                        {
                            // Display each file over 10 MB; 
                            if (fi.Length > 10000000)
                            {
                                Console.WriteLine("{0}\t\t{1}",  fi.FullName, fi.Length.ToString("N0"));
                            }
                        }
                        catch (UnauthorizedAccessException UnAuthFile)
                        {
                            Console.WriteLine("UnAuthFile: {0}", UnAuthFile.Message);
                        }
                    }
                }
                catch (UnauthorizedAccessException UnAuthSubDir)
                {
                    Console.WriteLine("UnAuthSubDir: {0}", UnAuthSubDir.Message);
                }
            }
        }
        catch (DirectoryNotFoundException DirNotFound)
        {
            Console.WriteLine("{0}", DirNotFound.Message);
        }
        catch (UnauthorizedAccessException UnAuthDir)
        {
            Console.WriteLine("UnAuthDir: {0}", UnAuthDir.Message);
        }
        catch (PathTooLongException LongPath)
        {
            Console.WriteLine("{0}", LongPath.Message);
        }
    }
}