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 {}
解決
再帰を手動で行う必要があります。 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
}
}
}
}
他のヒント
この単純な関数はうまく機能し、質問の要件を満たしています。
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;
}
また、方法:ディレクトリツリーを反復処理も確認してください。
これを行う簡単な方法は、ファイルのリストとディレクトリのキューを使用することです。 メモリを節約します。 再帰プログラムを使用して同じタスクを実行すると、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;
}
手順:
- キューにルートをエンキューします
- ループで、キューから取り出し、そのディレクトリ内のファイルをリストに追加し、サブフォルダをキューに追加します。
- キューが空になるまで繰り返します。
この質問はやや古いことはわかっていますが、今日も同じ問題が発生しており、「フォルダ再帰」ソリューションの詳細を説明する次の記事を見つけました。
この記事は、 GetDirectories()
メソッドの欠陥を認めています...:
残念ながら、この[ GetDirectories()メソッドの使用 ]には問題があります。これらの中で重要なのは、 読み込もうとするフォルダーは、 現在のユーザーはアクセスできません。フォルダーを無視するのではなく アクセスが制限されている場合、メソッドは UnauthorizedAccessException。ただし、この問題を回避できます 独自の再帰フォルダー検索コードを作成します。
...そして、ソリューションの詳細を紹介します:
これで質問に答えるはずです。サブディレクトリを経由する問題を無視しましたが、それがわかっていると思います。
もちろん、このために別のメソッドを用意する必要はありませんが、パスが有効であることを確認し、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?
}
}
UnauthorisedAccessException問題を処理するソリューションについては、 https://stackoverflow.com/a/10728792/89584 を参照してください。
上記のすべてのソリューションは、GetFiles()またはGetDirectories()の呼び出しが許可の混在したフォルダーに対して行われた場合、ファイルやディレクトリを失います。
これは、フル機能の.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); }
}
}
}
}