'Ignore exceptions of Directory.EnumerateFiles, skip those files
While enumerating I would like to Skip/Ignore exception.
I try to add a try catch in the selector:
static IEnumerable<string> GetSafeAllFiles
(string path, string searchPattern, SearchOption searchOption = SearchOption.AllDirectories)
{
return Directory.EnumerateFiles(path, searchPattern, searchOption)
.Select(f =>
{
try
{
return f;
}
catch (Exception e)
{
return string.Empty;
}
});
}
I try using a solution from an accepted answer:
var test23 = Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories)
.SkipExceptions().Take(100);
With no result, as it will stop after the first error. So I try to implement my own :
static IEnumerable<string> test12(string path, string searchPattern, SearchOption searchOption = SearchOption.AllDirectories)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
}
if (string.IsNullOrEmpty(searchPattern))
{
throw new ArgumentNullException("searchPattern");
}
Queue<string> stillToProcess = new Queue<string>(new[] { path });
foreach (var dir in Directory.EnumerateDirectories(path))
{
stillToProcess.Enqueue(dir);
}
while (stillToProcess.Count > 0)
{
string currentPath = stillToProcess.Dequeue();
IEnumerable<string> ret = Enumerable.Empty<string>();
try
{
ret = Directory.EnumerateFiles(currentPath, searchPattern);
}
catch (UnauthorizedAccessException e)
{ }
// yield! keyword
foreach (var i in ret) { yield return i; }
}
yield break;
}
But it skip directory if there is one error. When I want to skip only the error file.
In order to test, possible solution please do it on c:\$Recycle.Bin, as it's the easiest source of UnauthorizedAccessException.
Solution 1:[1]
Some times ago, I wrote this piece of code, to get all files in a directory and all subdirectories. I would go this way to achieve your goal. You have to customize it to work with search pattern and search options. Otherwise, the CanRead method could be solve your problem, by preventing the code to throw exceptions overall.
public class DirectoryAnalyser
{
private List<string> _files;
private int _directoryCounter;
public async Task<List<string>> GetFilesAsync(string directory, CancellationTokenSource cancellationToken, IProgress<DirectoryAnalyserProgress> progress)
{
this._files = new List<string>();
this._directoryCounter = 0;
await this.GetFilesInternalAsync(directory, cancellationToken, progress);
return this._files;
}
private async Task GetFilesInternalAsync(string directory, CancellationTokenSource cancellationToken, IProgress<DirectoryAnalyserProgress> progress)
{
if (cancellationToken.IsCancellationRequested)
{
return;
}
if (!this.CanRead(directory))
{
return;
}
this._files.AddRange(Directory.GetFiles(directory));
this._directoryCounter++;
progress?.Report(new DirectoryAnalyserProgress()
{
DirectoriesSearched = this._directoryCounter,
FilesFound = this._files.Count
});
foreach (var subDirectory in Directory.GetDirectories(directory))
{
await this.GetFilesInternalAsync(subDirectory, cancellationToken, progress);
}
}
public bool CanRead(string path)
{
var readAllow = false;
var readDeny = false;
var accessControlList = Directory.GetAccessControl(path);
if (accessControlList == null)
{
return false;
}
var accessRules = accessControlList.GetAccessRules(true, true, typeof(SecurityIdentifier));
if (accessRules == null)
{
return false;
}
foreach (FileSystemAccessRule rule in accessRules)
{
if ((FileSystemRights.Read & rule.FileSystemRights) != FileSystemRights.Read)
{
continue;
}
if (rule.AccessControlType == AccessControlType.Allow)
{
readAllow = true;
}
else if (rule.AccessControlType == AccessControlType.Deny)
{
readDeny = true;
}
}
return readAllow && !readDeny;
}
}
public class DirectoryAnalyserProgress
{
public int FilesFound { get; set; }
public int DirectoriesSearched { get; set; }
}
Solution 2:[2]
This is what i came up with:
public static IEnumerable<FileInfo> EnumerateFilesIgnoreErrors(IEnumerable<FileInfo> files)
{
using (var e1 = files.GetEnumerator())
{
while (true)
{
FileInfo cur = null;
try
{
// MoveNext() can throw an Exception
if (! e1.MoveNext())
break;
cur = e1.Current;
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
if (cur != null)
{
yield return cur;
}
}
}
}
Use like this:
var myFiles = new DirectoryInfo(@"C:\")
.EnumerateFiles("*", SearchOption.AllDirectories);
foreach (FileInfo fi in EnumerateFilesIgnoreErrors(myFiles))
{
Debug.WriteLine(fi.Name);
}
You can also use it like this:
var myList = EnumerateFilesIgnoreErrors(myFiles).ToList();
Works on "c:\$Recycle.Bin" etc
Ignores UnauthorizedAccessException
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |
