Игнорировать папки / файлы, когда 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 {}
11 ответов
Вы должны будете сделать рекурсию вручную; не используйте AllDirectories - просматривайте одну папку за раз, затем попробуйте получить файлы из подкаталогов. Не проверено, но что-то вроде ниже (примечание использует делегат, а не строит массив):
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
}
}
}
}
Начиная с.NET Standard 2.1, теперь вы можете просто делать:
var filePaths = Directory.EnumerateFiles(@"C:\my\files", "*.xml", new EnumerationOptions
{
IgnoreInaccessible = true,
RecurseSubdirectories = true
});
Согласно документам MSDN об IgnoreInaccessible:
Возвращает или задает значение, указывающее, следует ли пропускать файлы или каталоги при отказе в доступе (например, UnauthorizedAccessException или SecurityException). По умолчанию это правда.
Значение по умолчанию действительно истинно, но я оставил его здесь, чтобы показать свойство.
Такая же перегрузка доступна для DirectoryInfo
также.
Эта простая функция работает хорошо и отвечает требованиям вопросов.
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?
}
}
Вот полнофункциональная, .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); }
}
}
}
}
См. /questions/23255787/neobhodimo-vozobnovit-popyitku-posle-blokirovaniya-catch/23255799#23255799 для решения, которое решает проблему UnauthorisedAccessException.
Все приведенные выше решения будут пропускать файлы и / или каталоги, если какие-либо вызовы GetFiles() или GetDirectories() находятся в папках с различными разрешениями.
Для тех, кому нужна платформа ниже NET 2.1, просто установите Microsoft.IO.Redist на nuget.
var filePaths = Directory.EnumerateFiles(@"C:\my\files", "*.xml", new EnumerationOptions
{
IgnoreInaccessible = true,
RecurseSubdirectories = true
});
Я предпочитаю использовать функции фреймворка C#, но нужная мне функция будет включена в.NET framework 5.0, поэтому мне нужно написать ее.
// 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;
}