检查文件夹是否包含文件

时间:2010-04-26 10:51:53

标签: c# directory

我有程序写入数据库文件夹已满或为空。现在我正在使用

bool hasFiles=false;
(Directory.GetFiles(path).Length >0) ? hasFiles=true: hasFiles=false;

但这需要将近一个小时,而且我现在无法做任何事情。

有没有最快的方法来检查文件夹是否有任何文件?

6 个答案:

答案 0 :(得分:10)

要检查目录或子目录中是否存在任何文件,在.net 4中,您可以使用以下方法:

public bool isDirectoryContainFiles(string path) {
    if (!Directory.Exists(path)) return false;
    return Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories).Any();
}

答案 1 :(得分:5)

加速这种跨网络搜索的关键是减少网络上的请求数量。而不是获取所有目录,然后检查每个目录,尝试通过一次调用获取所有内容。

在.NET 3.5中,没有一种方法可以递归获取所有文件和文件夹,因此您必须自己构建它(见下文)。在.NET 4中,只需一步就可以实现新的重载。

使用DirectoryInfo还可以获取有关返回的名称是文件还是目录的信息,这也会减少调用。

这意味着拆分所有目录和文件的列表就像这样:

struct AllDirectories {
  public List<string> DirectoriesWithoutFiles { get; set; }
  public List<string> DirectoriesWithFiles { get; set; }
}

static class FileSystemScanner {
  public AllDirectories DivideDirectories(string startingPath) {
    var startingDir = new DirectoryInfo(startingPath);

    // allContent IList<FileSystemInfo>
    var allContent = GetAllFileSystemObjects(startingDir);
    var allFiles = allContent.Where(f => !(f.Attributes & FileAttributes.Directory))
                             .Cast<FileInfo>();
    var dirs = allContent.Where(f => (f.Attributes & FileAttributes.Directory))
                         .Cast<DirectoryInfo>();
    var allDirs = new SortedList<DirectoryInfo>(dirs, new FileSystemInfoComparer());

    var res = new AllDirectories {
      DirectoriesWithFiles = new List<string>()
    };
    foreach (var file in allFiles) {
      var dirName = Path.GetDirectoryName(file.Name);
      if (allDirs.Remove(dirName)) {
        // Was removed, so first time this dir name seen.
        res.DirectoriesWithFiles.Add(dirName);
      }
    }
    // allDirs now just contains directories without files
    res.DirectoriesWithoutFiles = new List<String>(addDirs.Select(d => d.Name));
  }

  class FileSystemInfoComparer : IComparer<FileSystemInfo> {
    public int Compare(FileSystemInfo l, FileSystemInfo r) {
      return String.Compare(l.Name, r.Name, StringComparison.OrdinalIgnoreCase);
    }
  }
}

实现GetAllFileSystemObjects取决于.NET版本。在.NET 4上,它非常简单:

ILIst<FileSystemInfo> GetAllFileSystemObjects(DirectoryInfo root) {
  return root.GetFileSystemInfos("*.*", SearchOptions.AllDirectories);
}

在早期版本中,还需要做一些工作:

ILIst<FileSystemInfo> GetAllFileSystemObjects(DirectoryInfo root) {
  var res = new List<FileSystemInfo>();
  var pending = new Queue<DirectoryInfo>(new [] { root });

  while (pending.Count > 0) {
    var dir = pending.Dequeue();
    var content = dir.GetFileSystemInfos();
    res.AddRange(content);
    foreach (var dir in content.Where(f => (f.Attributes & FileAttributes.Directory))
                               .Cast<DirectoryInfo>()) {
      pending.Enqueue(dir);
    }
  }

  return res;
}

这种方法尽可能少地调用文件系统,只在.NET 4上调用一次,或在早期版本的每个目录调用一次,允许网络客户端和服务器最小化底层文件系统调用和网络往返次数。

获取FileSystemInfo实例的缺点是需要多个文件系统操作(我相信这在某种程度上取决于操作系统),但是对于每个名称,任何解决方案都需要知道它是文件还是目录,所以这是不可避免的在某种程度上(不使用FindFileFirst / FindNextFile / FindClose的P / Invoke。)


除此之外,使用分区扩展方法会更容易:

Tuple<IEnumerable<T>,IEnumerable<T>> Extensions.Partition<T>(
                                                 this IEnumerable<T> input,
                                                 Func<T,bool> parition);

写那个懒惰是一个有趣的练习(只有当某些东西在其中一个输出上迭代时才会消耗输入,同时缓冲另一个输出)。

答案 2 :(得分:4)

如果您使用.Net 4.0,请查看EnumerateFiles方法。 http://msdn.microsoft.com/en-us/library/dd413232(v=VS.100).aspx

  

EnumerateFiles和GetFiles   方法的不同之处如下:当你   使用EnumerateFiles,你就可以开始了   枚举FileInfo的集合   整个集合之前的对象   回;当你使用GetFiles时,你   必须等待整个阵列   要返回的FileInfo对象   在您可以访问阵列之前。   因此,当你工作时   许多文件和目录,   EnumerateFiles可以更有效。

这样就不会从文件夹中检索所有文件,如果枚举器至少有1个文件,则该文件夹不为空

答案 3 :(得分:3)

我假设(虽然我不确定),因为你在网络驱动器上调用GetFiles(),它会增加大量时间从所有30k文件夹中检索所有文件并通过它们进行枚举。

我在CodeProject上找到了另一个目录枚举器here,看起来很有希望。

或者......你可以在服务器上创建一个WebService,为你枚举所有内容并在之后返回结果。

编辑:我认为您的问题更有可能是文件夹访问权限。每次访问网络驱动器中的目录时,您都将进行安全性和权限检查。 * 30k文件夹将成为一个重大的性能。我非常怀疑使用FindFirstFile会有多大帮助,因为枚举的文件实际数量只会是0或1。

答案 4 :(得分:2)

可能值得一提:

  

但是这需要将近一个小时,我现在无法做任何事情。 (重点补充)

你是在主要线程的GUI应用程序中这样做的吗?如果是这样,请使用BackgroundWorker吐出此过程。至少那时应用程序将继续响应。您还可以在方法中添加CancellationPending的检查,如果花费的时间过长则取消它。

与你的问题相关 - 只是我注意到并认为我会评论的内容。

答案 5 :(得分:0)

您最好的选择是使用API​​函数FindFirstFile。它不会花费那么长的时间。