Быстро получить все файлы и каталоги по указанному пути - PullRequest
67 голосов
/ 19 мая 2011

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

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

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

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");
            }

Если произойдет исключение, я не получу файлы.

Ответы [ 7 ]

44 голосов
/ 19 мая 2011

Этот метод намного быстрее.Вы можете звонить только при размещении большого количества файлов в каталоге.Мой внешний жесткий диск 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);                    
    }

}

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

17 голосов
/ 19 мая 2011

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

11 голосов
/ 17 сентября 2012

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

Лучшее, что я смог сделать, - это обернуть его в моей библиотеке и показать как FindFile ( source *)1006 *) класса в пространстве имен 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 , поэтому убедитесь, что вы не сохраняете ссылку на него, так как его значения будут изменяться для каждого вызванного события.

2 голосов
/ 03 ноября 2014

Может быть, это будет полезно для вас.Вы можете использовать метод " 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);
        }
    }
}
2 голосов
/ 20 октября 2012

Я знаю, что это старый, но ... Другой вариант может заключаться в том, чтобы использовать 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);
        }
    }

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

2 голосов
/ 17 сентября 2012

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

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

Быстроперечисление файлов

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

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

2 голосов
/ 14 мая 2012

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

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
}
...