忽略Directory.EnumerateFiles的异常,跳过这些文件

时间:2018-10-23 12:54:53

标签: c#

在枚举时,我想跳过/忽略异常。

我尝试在选择器中添加一个try catch:

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

我尝试使用accepted answer中的解决方案:

var test23 = Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories)
                      .SkipExceptions().Take(100);

没有结果,因为它将在第一个错误后停止。所以我尝试实现自己的:

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

但是如果有一个错误,它将跳过目录。当我只想跳过错误文件时。

为了进行测试,可能的解决方案请在c:\$Recycle.Bin上进行操作,因为它是UnauthorizedAccessException的最简单来源。

1 个答案:

答案 0 :(得分:0)

有时候,我编写了这段代码,以获取目录和所有子目录中的所有文件。我会以这种方式实现您的目标。您必须对其进行自定义才能与搜索模式和搜索选项一起使用。否则,通过防止代码整体抛出异常,CanRead方法可以解决您的问题。

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