web-dev-qa-db-fra.com

Obtenez rapidement tous les fichiers et répertoires dans un chemin spécifique

Je crée une application de sauvegarde où c # analyse un répertoire. Avant d’avoir quelque chose comme cela afin d’obtenir tous les fichiers et sous-fichiers dans un répertoire:

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
}

Le seul problème avec cela est qu'il est parfois impossible d'accéder à un fichier et que je reçois plusieurs erreurs. un exemple d'erreur que je reçois est:error

En conséquence, j'ai créé une méthode récursive qui analysera tous les fichiers du répertoire actuel. S'il y a des répertoires dans ce répertoire, la méthode sera appelée à nouveau en passant par ce répertoire. La bonne chose à propos de cette méthode est que je pouvais placer les fichiers dans un bloc try catch en me donnant la possibilité d’ajouter ces fichiers à une liste s’il n’y avait pas d’erreurs et d’ajouter le répertoire à une autre liste si j’avais des erreurs.

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

Donc, cette méthode fonctionne très bien, le seul problème est que, lorsque je parcours un grand répertoire, cela prend beaucoup de temps. Comment pourrais-je accélérer ce processus? Ma méthode actuelle est la suivante au cas où vous en auriez besoin.

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 le problème si j'essaie de gérer l'exception avec quelque chose comme:

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

Est-ce que si une exception se produit alors je ne reçois aucun fichier.

67
Tono Nam

Cette méthode est beaucoup plus rapide. Vous ne pouvez téléphoner que lorsque vous placez beaucoup de fichiers dans un répertoire. Mon disque dur externe A:\contient presque 1 terabit, ce qui fait une grande différence lorsque vous traitez beaucoup de fichiers.

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

}

En passant, j'ai eu ceci grâce à votre commentaire Jim Mischel

44
Tono Nam

Dans .NET 4.0, il existe la méthode Directory.EnumerateFiles qui renvoie un IEnumerable<string> et ne charge pas tous les fichiers en mémoire. Ce n'est qu'une fois que vous commencez à parcourir la collection renvoyée que les fichiers seront renvoyés et les exceptions peuvent être gérées .

17
Darin Dimitrov

Les méthodes d'énumération de fichiers .NET ont une longue histoire de lenteur. Le problème est qu'il n'y a pas de moyen instantané d'énumérer de grandes structures de répertoires. Même la réponse acceptée ici a ses problèmes avec les allocations du GC.

Le mieux que j'ai pu faire est résumé dans ma bibliothèque et exposé en tant que classe FindFile ( source ) dans CSharpTest.Net.IO espace de noms. Cette classe peut énumérer des fichiers et des dossiers sans attributions inutiles au GC ni classement des chaînes.

L'utilisation est assez simple et la propriété RaiseOnAccessDenied ignorera les répertoires et les fichiers auxquels l'utilisateur n'a pas accès:

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

Pour mon lecteur C:\local, cela génère les éléments suivants:

Énuméré 810 046 fichiers totalisant 307 707 792 662 octets en 232,876 secondes.

Votre kilométrage peut varier en fonction de la vitesse du lecteur, mais c'est la méthode la plus rapide que j'ai trouvée pour énumérer des fichiers dans du code géré. Le paramètre event est une classe de type en mutation FindFile.FileFoundEventArgs , assurez-vous donc de ne pas y faire référence car ses valeurs changeront pour chaque événement déclenché.

12
csharptest.net

Peut-être que cela vous sera utile. Vous pouvez utiliser la méthode " DirectoryInfo.EnumerateFiles " et gérer UnauthorizedAccessException selon vos besoins.

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
Yuriy A.

Je sais que c'est vieux, mais ... Une autre option peut être d'utiliser FileSystemWatcher comme ceci:

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

Cela vous permettrait de surveiller les répertoires pour les modifications de fichiers avec un processus extrêmement léger, que vous pourriez ensuite utiliser pour stocker les noms des fichiers modifiés afin de pouvoir les sauvegarder au moment opportun.

2
TChadwick

(copié cette partie de mon autre réponse dans votre autre question)

Afficher la progression lors de la recherche de tous les fichiers d'un répertoire

Enumération rapide des fichiers

Bien sûr, comme vous le savez déjà, il existe de nombreuses façons de procéder au dénombrement lui-même ... mais aucune ne sera instantanée. Vous pouvez essayer d'utiliser le SN Journal du système de fichiers pour effectuer l'analyse. Examinez ce projet dans CodePlex: Scanner MFT dans VB.NET ... il a trouvé tous les fichiers de mon IDE SATA (pas SSD) en moins plus de 15 secondes et 311000 fichiers trouvés.

Vous devrez filtrer les fichiers par chemin, afin que seuls les fichiers du chemin recherché soient renvoyés. Mais c’est la partie la plus facile du travail!

2
Miguel Angelo

Vous pouvez l'utiliser pour obtenir tous les répertoires et sous-répertoires. Ensuite, il vous suffit de faire une boucle pour traiter les fichiers.

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
}
2
Zombie