检查文件夹是否有文件

发布于 2024-08-30 06:11:17 字数 219 浏览 2 评论 0原文

我有一个程序可以将哪些文件夹已满或为空写入数据库。现在我在用

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

但是需要快一个小时,而且这段时间我什么也做不了。

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

I have program which writes to database which folders are full or empty. Now I'm using

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

but it takes almost one hour, and I can't do anything in this time.

Is there any fastest way to check if folder has any file ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

甩你一脸翔 2024-09-06 06:11:17

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

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

To check if any files exists inside the directory or sub directories, in .net 4, you can use method below:

public bool isDirectoryContainFiles(string path) {
    if (!Directory.Exists(path)) return false;
    return Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories).Any();
}
故笙诉离歌 2024-09-06 06:11:17

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

在 .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);

将其编写为惰性将是一项有趣的练习(仅当某个输出迭代一个输出时才消耗输入,同时缓冲另一个输出)。

The key to speeding up such a cross-network search is to cut down the number of requests across the network. Rather than getting all the directories, and then checking each for files, try and get everything from one call.

In .NET 3.5 there is no one method to recursively get all files and folders, so you have to build it yourself (see below). In .NET 4 new overloads exist to to this in one step.

Using DirectoryInfo one also gets information on whether the returned name is a file or directory, which cuts down calls as well.

This means splitting a list of all the directories and files becomes something like this:

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

Implementing GetAllFileSystemObjects depends on the .NET version. On .NET 4 it is very easy:

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

On earlier versions a little more work is needed:

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

This approach calls into the filesystem as few times as possible, just once on .NET 4 or once per directory on earlier versions, allowing the network client and server to minimise the number of underlying filesystem calls and network round trips.

Getting FileSystemInfo instances has the disadvantage of needing multiple file system operations (I believe this is somewhat OS dependent), but for each name any solution needs to know if it is a file or directory so this is not avoidable at some level (without resorting to P/Invoke of FindFileFirst/FindNextFile/FindClose).


Aside, the above would be easier with a partition extension method:

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

Writing that to be lazy would be an interesting exercise (only consuming input when something iterates over one of the outputs, while buffering the other).

虐人心 2024-09-06 06:11:17

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

EnumerateFiles 和 GetFiles
方法的不同之处如下: 当您
使用EnumerateFiles,就可以启动
枚举 FileInfo 的集合
整个集合之前的对象
返回;当你使用 GetFiles 时,你
必须等待整个数组
要返回的 FileInfo 对象
在您可以访问该数组之前。
因此,当您与
许多文件和目录,
EnumerateFiles 可以更高效。

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

If you are using .Net 4.0 have a look at the EnumerateFiles method.
http://msdn.microsoft.com/en-us/library/dd413232(v=VS.100).aspx

The EnumerateFiles and GetFiles
methods differ as follows: When you
use EnumerateFiles, you can start
enumerating the collection of FileInfo
objects before the whole collection is
returned; when you use GetFiles, you
must wait for the whole array of
FileInfo objects to be returned
before you can access the array.
Therefore, when you are working with
many files and directories,
EnumerateFiles can be more efficient.

This way not all the files are retrieved from the folder, if the enumerator has at least 1 file the folder is not empty

长途伴 2024-09-06 06:11:17

我假设(尽管我不确定)因为您在网络驱动器上调用 GetFiles(),所以从所有 30k 文件夹中检索所有文件并枚举它们会增加相当多的时间。

我在此处找到了替代目录枚举器在 CodeProject 上,看起来很有希望。

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

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

I'm assuming (although I don't know for definite) that because you're calling GetFiles() on a network drive it adds considerable time to retrieve all the files from all 30k folders and enumerate through them.

I've found an alternative Directory Enumerator here on CodeProject which looks promising.

Alternatively... you could create a WebService on the server that enumerates everything for you and returns the results after.

EDIT: I think your problem is more likely the folder access. Each time you access a Directory in the network drive you're going to be hitting security and permission checks. That * 30k folders will be a big performance hit. I highly doubt using the FindFirstFile will help much as the actual number of files enumerated will only ever be 0 or 1.

甜点 2024-09-06 06:11:17

可能值得一提的是:

但是需要将近一个小时,并且这段时间我什么也做不了。 (强调已添加)

您是在主线程上从 GUI 应用程序执行此操作吗?如果是这样,请使用 BackgroundWorker 结束此过程。至少这样应用程序将继续响应。您还可以在该方法中添加对 CancellationPending 的检查,并在花费太长时间时取消它。

有点与你的问题无关——只是我注意到并认为我会发表评论的事情。

Might be worth mentioning:

but it takes almost one hour, and I can't do anything in this time. (emphasis added)

Are you doing this from a GUI app, on the main thread? If so, spit this process off using a BackgroundWorker. At least then the app will continue to be responsive. You could also add checks for CancellationPending in the method and cancel it if it's taking too long.

Kind of tangential to your question--just something I noticed and thought I'd comment on.

灯下孤影 2024-09-06 06:11:17

最好的选择是使用 API 函数 FindFirstFile。那样就不会花那么长时间了。

Your best bet is to use the API function FindFirstFile. It wont take nearly as long then.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文