web-dev-qa-db-ja.com

Directory.GetFiles()がアクセスを拒否されたときにフォルダー/ファイルを無視する

選択したディレクトリ(およびオプションでサブディレクトリ)で見つかったすべてのファイルのリストを表示しようとしています。私が抱えている問題は、GetFiles()メソッドがアクセスできないフォルダーに遭遇すると、例外をスローしてプロセスが停止することです。

この例外を無視して(保護されたフォルダー/ファイルを無視して)、アクセス可能なファイルをリストに追加し続けるにはどうすればよいですか?

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 {}
71
Rowan

再帰を手動で行う必要があります。 AllDirectoriesを使用しないでください-一度に1つのフォルダーを探し、サブディレクトリからファイルを取得してください。テストされていませんが、以下のようなものです(注:配列を構築するのではなく、デリゲートを使用しています):

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
            }
        }
    }
}
48
Marc Gravell

この単純な機能はうまく機能し、質問の要件を満たしています。

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

    try 
    { 
        files.AddRange(Directory.GetFiles(path, pattern, SearchOption.TopDirectoryOnly));
        foreach (var directory in Directory.GetDirectories(path))
            files.AddRange(GetFiles(directory, pattern));
    } 
    catch (UnauthorizedAccessException) { }

    return files;
}
12
Ben Gripka

これを行う簡単な方法は、ファイルのリストとディレクトリのキューを使用することです。メモリを節約します。再帰プログラムを使用して同じタスクを実行すると、OutOfMemory例外がスローされる可能性があります。出力:リストに追加されたファイルは、上から下(幅が最初)のディレクトリツリーに従って編成されます。

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

手順:

  1. ルートをキューに入れます
  2. ループで、それをデキューし、そのディレクトリ内のファイルをリストに追加し、サブフォルダーをキューに追加します。
  3. キューが空になるまで繰り返します。
5
Shubham

私はこの質問がやや古いことを知っていますが、今日この同じ問題を抱えていて、「フォルダ再帰」ソリューションを詳細に説明する次の記事を見つけました。

この記事はGetDirectories()メソッドの欠陥を認めています...:

残念ながら、この[GetDirectories()メソッドの使用]には問題があります。中でも重要なのは、現在のユーザーがアクセスできないように、読み取ろうとするフォルダーの一部を構成できることです。アクセスが制限されているフォルダーを無視するのではなく、メソッドはUnauthorizedAccessExceptionをスローします。ただし、独自の再帰的なフォルダー検索コードを作成することにより、この問題を回避できます。

...そして、ソリューションを詳細に紹介します。

http://www.blackwasp.co.uk/FolderRecursion.aspx

3
sergeidave

これで質問に答えるはずです。サブディレクトリを経由する問題を無視しましたが、それがわかっていると思います。

もちろん、このために別のメソッドを用意する必要はありませんが、パスが有効であることを確認し、GetFiles()を呼び出すときに発生する可能性のある他の例外に対処するための便利な場所を見つけるかもしれません。

お役に立てれば。

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?
    }
}
2
user25306

unauthorisedAccessException問題を処理するソリューションについては、 https://stackoverflow.com/a/10728792/89584 を参照してください。

上記のすべてのソリューションは、GetFiles()またはGetDirectories()の呼び出しが許可の混在したフォルダーに対して行われた場合、ファイルやディレクトリを失います。

1
Malcolm

以下に、フル機能の.NET 2.0互換の実装を示します。

生成されたファイルのListを変更して、FileSystemInfoバージョンのディレクトリをスキップすることもできます。

null値に注意してください!)

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
Mehrdad