Ignorieren Sie Ordner / Dateien, wenn Directory.GetFiles () der Zugriff verweigert wird


73

Ich versuche, eine Liste aller Dateien anzuzeigen, die im ausgewählten Verzeichnis gefunden wurden (und optional alle Unterverzeichnisse). Das Problem, das ich habe, ist, dass wenn die GetFiles () -Methode auf einen Ordner stößt, auf den sie nicht zugreifen kann, eine Ausnahme ausgelöst wird und der Prozess gestoppt wird.

Wie ignoriere ich diese Ausnahme (und ignoriere den geschützten Ordner / die geschützte Datei) und füge der Liste weiterhin zugängliche Dateien hinzu?

try
{
    if (cbSubFolders.Checked == false)
    {
        string[] files = Directory.GetFiles(folderBrowserDialog1.SelectedPath);
        foreach (string fileName in files)
            ProcessFile(fileName);
    }
    else
    {
        string[] files = Directory.GetFiles(folderBrowserDialog1.SelectedPath, "*.*", SearchOption.AllDirectories);
        foreach (string fileName in files)
            ProcessFile(fileName);
    }
    lblNumberOfFilesDisplay.Enabled = true;
}
catch (UnauthorizedAccessException) { }
finally {}

Antworten:


48

Sie müssen die Rekursion manuell durchführen. Verwenden Sie nicht AllDirectories - schauen Sie sich jeweils einen Ordner an und versuchen Sie dann, die Dateien aus Unterverzeichnissen abzurufen. Ungetestet, aber so ähnlich wie unten (Hinweis verwendet einen Delegaten, anstatt ein Array zu erstellen):

using System;
using System.IO;
static class Program
{
    static void Main()
    {
        string path = ""; // TODO
        ApplyAllFiles(path, ProcessFile);
    }
    static void ProcessFile(string path) {/* ... */}
    static void ApplyAllFiles(string folder, Action<string> fileAction)
    {
        foreach (string file in Directory.GetFiles(folder))
        {
            fileAction(file);
        }
        foreach (string subDir in Directory.GetDirectories(folder))
        {
            try
            {
                ApplyAllFiles(subDir, fileAction);
            }
            catch
            {
                // swallow, log, whatever
            }
        }
    }
}

Zu gut, ich habe so etwas in VB.NET nicht gefunden. Ich hoffe, es macht Ihnen nichts aus, wenn ich dies hier in VB.NET übersetzt habe
Steve

8
immer noch nicht genug: GetFiles wird intern ausgelöst, wenn nur auf eine Datei in einem Ordner nicht zugegriffen werden kann. Der gesamte Ordner bleibt also unbehandelt.
v.oddou

Es ist jedoch erheblich langsamer.
Meghdad

Ich bin mir nicht sicher, wie dies die akzeptierte Antwort ist. GetFilesoder GetDirectoriesbeide können Ausnahmen auslösen, sodass Sie nach der Ausnahme nichts mehr sehen.
HackSlash

@HackSlash Der Kommentar "schlucken, protokollieren, was auch immer" ist eine Einladung für Sie, die für Ihr Szenario geeignete Behandlung hinzuzufügen
Marc Gravell

14

Diese einfache Funktion funktioniert gut und erfüllt die Anforderungen der Fragen.

private List<string> GetFiles(string path, string pattern)
{
    var files = new List<string>();
    var directories = new string[] { };

    try
    {
        files.AddRange(Directory.GetFiles(path, pattern, SearchOption.TopDirectoryOnly));
        directories = Directory.GetDirectories(path);
    }
    catch (UnauthorizedAccessException) { }

    foreach (var directory in directories)
        try
        {
            files.AddRange(GetFiles(directory, pattern));
        }
        catch (UnauthorizedAccessException) { }

    return files;
}

1
Ja, wegen der fehlenden Fehlerbehandlung ist es nicht sehr nützlich. Versuchen Sie einfach, den gesamten c: \ -Baum zu durchsuchen. Es gibt eine Reihe von Bereichen im Windows-Dateisystem, in denen der Benutzer möglicherweise selbst mit Administratorrechten nicht über ausreichende Zugriffsrechte verfügt. Darum geht es hier (neben Knotenpunkten und so weiter).
Philm

Selbes Problem hier. Wenn Sie eine Ausnahme im GetFilesBefehl auslösen, können Sie danach nichts mehr sehen.
HackSlash

@ HackSlash Bitte versuchen Sie es mit den gerade vorgenommenen Änderungen, damit die Suche fortgesetzt werden kann, nachdem ein Verzeichnis den Zugriff verweigert.
Ben Gripka

8

Eine einfache Möglichkeit hierfür ist die Verwendung einer Liste für Dateien und einer Warteschlange für Verzeichnisse. Es schont das Gedächtnis. Wenn Sie ein rekursives Programm verwenden, um dieselbe Aufgabe auszuführen, kann dies zu einer OutOfMemory-Ausnahme führen. Die in der Liste hinzugefügten Ausgabedateien sind nach dem Verzeichnisbaum von oben nach unten (Breite zuerst) organisiert.

public static List<string> GetAllFilesFromFolder(string root, bool searchSubfolders) {
    Queue<string> folders = new Queue<string>();
    List<string> files = new List<string>();
    folders.Enqueue(root);
    while (folders.Count != 0) {
        string currentFolder = folders.Dequeue();
        try {
            string[] filesInCurrent = System.IO.Directory.GetFiles(currentFolder, "*.*", System.IO.SearchOption.TopDirectoryOnly);
            files.AddRange(filesInCurrent);
        }
        catch {
            // Do Nothing
        }
        try {
            if (searchSubfolders) {
                string[] foldersInCurrent = System.IO.Directory.GetDirectories(currentFolder, "*.*", System.IO.SearchOption.TopDirectoryOnly);
                foreach (string _current in foldersInCurrent) {
                    folders.Enqueue(_current);
                }
            }
        }
        catch {
            // Do Nothing
        }
    }
    return files;
}

Schritte:

  1. Stellen Sie den Stamm in die Warteschlange
  2. In einer Schleife die Warteschlange entfernen, die Dateien in diesem Verzeichnis zur Liste hinzufügen und die Unterordner zur Warteschlange hinzufügen.
  3. Wiederholen, bis die Warteschlange leer ist.

4

Seit .NET Standard 2.1 können Sie jetzt einfach Folgendes tun:

var filePaths = Directory.EnumerateFiles(@"C:\my\files", "*.xml", new EnumerationOptions
{
    IgnoreInaccessible = true,
    RecurseSubdirectories = true
});

Laut den MSDN-Dokumenten zu IgnoreInaccessible :

Ruft einen Wert ab oder legt einen Wert fest, der angibt, ob Dateien oder Verzeichnisse übersprungen werden sollen, wenn der Zugriff verweigert wird (z. B. UnauthorizedAccessException oder SecurityException). Der Standardwert ist true.

Der Standardwert ist tatsächlich wahr, aber ich habe ihn hier beibehalten, um die Eigenschaft anzuzeigen.

Die gleiche Überlastung ist auch für verfügbar DirectoryInfo.


Dies funktioniert auch in .NET Core 2.1, jedoch nicht in .NET Standard 2.0
Panagiotis Kanavos

Dies ist die einzige Antwort, die tatsächlich funktioniert, aber Sie müssen Ihr Ziel-Framework ändern.
HackSlash

2

Unter https://stackoverflow.com/a/10728792/89584 finden Sie eine Lösung für das Problem UnauthorisedAccessException.

Bei allen oben genannten Lösungen fehlen Dateien und / oder Verzeichnisse, wenn Aufrufe von GetFiles () oder GetDirectories () in Ordnern mit unterschiedlichen Berechtigungen erfolgen.


1
Alle Lösungen mit GetFiles / GetDirectories haben zwangsläufig das gleiche Problem und sind daher etwas unelegant
hello_earth

1

Dies sollte die Frage beantworten. Ich habe das Problem des Durchsuchens von Unterverzeichnissen ignoriert. Ich gehe davon aus, dass Sie das herausgefunden haben.

Natürlich benötigen Sie hierfür keine separate Methode, aber möglicherweise ist dies ein nützlicher Ort, um auch zu überprüfen, ob der Pfad gültig ist, und um die anderen Ausnahmen zu behandeln, die beim Aufrufen von GetFiles () auftreten können.

Hoffe das hilft.

private string[] GetFiles(string path)
{
    string[] files = null;
    try
    {
       files = Directory.GetFiles(path);
    }
    catch (UnauthorizedAccessException)
    {
       // might be nice to log this, or something ...
    }

    return files;
}

private void Processor(string path, bool recursive)
{
    // leaving the recursive directory navigation out.
    string[] files = this.GetFiles(path);
    if (null != files)
    {
        foreach (string file in files)
        {
           this.Process(file);
        }
    }
    else
    {
       // again, might want to do something when you can't access the path?
    }
}

Selbes Problem hier. Jedes Verzeichnis, das eine einzelne Ausnahme enthält, führt dazu, dass Sie die gesamte Verzeichnisliste verlieren.
HackSlash

1

Hier ist eine voll funktionsfähige, .NET 2.0-kompatible Implementierung.

Sie können sogar die Anzahl Listder Dateien ändern , um Verzeichnisse in der FileSystemInfoVersion zu überspringen !

(Vorsicht vor nullWerten!)

public static IEnumerable<KeyValuePair<string, string[]>> GetFileSystemInfosRecursive(string dir, bool depth_first)
{
    foreach (var item in GetFileSystemObjectsRecursive(new DirectoryInfo(dir), depth_first))
    {
        string[] result;
        var children = item.Value;
        if (children != null)
        {
            result = new string[children.Count];
            for (int i = 0; i < result.Length; i++)
            { result[i] = children[i].Name; }
        }
        else { result = null; }
        string fullname;
        try { fullname = item.Key.FullName; }
        catch (IOException) { fullname = null; }
        catch (UnauthorizedAccessException) { fullname = null; }
        yield return new KeyValuePair<string, string[]>(fullname, result);
    }
}

public static IEnumerable<KeyValuePair<DirectoryInfo, List<FileSystemInfo>>> GetFileSystemInfosRecursive(DirectoryInfo dir, bool depth_first)
{
    var stack = depth_first ? new Stack<DirectoryInfo>() : null;
    var queue = depth_first ? null : new Queue<DirectoryInfo>();
    if (depth_first) { stack.Push(dir); }
    else { queue.Enqueue(dir); }
    for (var list = new List<FileSystemInfo>(); (depth_first ? stack.Count : queue.Count) > 0; list.Clear())
    {
        dir = depth_first ? stack.Pop() : queue.Dequeue();
        FileSystemInfo[] children;
        try { children = dir.GetFileSystemInfos(); }
        catch (UnauthorizedAccessException) { children = null; }
        catch (IOException) { children = null; }
        if (children != null) { list.AddRange(children); }
        yield return new KeyValuePair<DirectoryInfo, List<FileSystemInfo>>(dir, children != null ? list : null);
        if (depth_first) { list.Reverse(); }
        foreach (var child in list)
        {
            var asdir = child as DirectoryInfo;
            if (asdir != null)
            {
                if (depth_first) { stack.Push(asdir); }
                else { queue.Enqueue(asdir); }
            }
        }
    }
}

0

Ich bevorzuge die Verwendung von c # Framework-Funktionen, aber die Funktion, die ich benötige, wird in .net Framework 5.0 enthalten sein, also muss ich sie schreiben.

// search file in every subdirectory ignoring access errors
    static List<string> list_files(string path)
    {
        List<string> files = new List<string>();

        // add the files in the current directory
        try
        {
            string[] entries = Directory.GetFiles(path);

            foreach (string entry in entries)
                files.Add(System.IO.Path.Combine(path,entry));
        }
        catch 
        { 
        // an exception in directory.getfiles is not recoverable: the directory is not accessible
        }

        // follow the subdirectories
        try
        {
            string[] entries = Directory.GetDirectories(path);

            foreach (string entry in entries)
            {
                string current_path = System.IO.Path.Combine(path, entry);
                List<string> files_in_subdir = list_files(current_path);

                foreach (string current_file in files_in_subdir)
                    files.Add(current_file);
            }
        }
        catch
        {
            // an exception in directory.getdirectories is not recoverable: the directory is not accessible
        }

        return files;
    }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.