【问题标题】:C# - Get LastWriteTime of A Folder's DescendantsC# - 获取文件夹后代的 LastWriteTime
【发布时间】:2013-04-18 17:54:23
【问题描述】:

如何检索文件夹的任何个后代的最新写入时间?

我需要一个方法来返回在指定日期时间之后修改的所有文件的路径。我想我可以通过确保目录LastWriteTime 在指定范围内,然后再遍历其文件和子目录的麻烦来节省大量昂贵的磁盘读取。

这是因为当目录顶层中的文件发生更改时,该文件夹的最后写入时间也会更新。但是,最后一次写入时间不会比文件的直接父级更远。换句话说,如果孙文件被更改,它的属性会被更新,它的父文件夹也是如此,但不是祖父文件夹。

我是否可以使用另一个高级指标来完成此操作,或者我是否必须求助于递归遍历每个文件夹而不考虑上次更改时间?

这是目前的方法:

private void AddAllFilesOfAllSubdirsWithFilterToList(string dirPath, ref List<FileInfo> filesList, DateTime minDate)
{
    // Return files in this directory level
    foreach (string filePath in Directory.GetFiles(dirPath, "*.*", SearchOption.TopDirectoryOnly))
    {
        FileInfo fileInfo = new FileInfo(filePath);
        if (fileInfo.LastWriteTimeUtc > minDate)
        {
            filesList.Add(fileInfo);
        }
    }

    // Return recursive searches through sudirs
    foreach (string subDirPath in Directory.GetDirectories(dirPath))
    {
        DirectoryInfo dirInfo = new DirectoryInfo(subDirPath);
        if (dirInfo.LastWriteTimeUtc > minDate)
        {
            GetAllFilesOfAllSubdirsWithFilter(subDirPath, ref filesList);
        }
    }
}

【问题讨论】:

  • 您声称更改文件会更新目录的 LastWriteTime,但我刚刚在 Windows 10 和 MacOS 11 上对此进行了测试,但没有看到。我更改了一个文件,但目录的最后写入时间保持不变。

标签: c# file-io directory


【解决方案1】:

很抱歉,但我认为您必须遍历所有子目录。

只需将上面代码中的 SearchOption 更改为通过子目录递归...

private void AddAllFilesOfAllSubdirsWithFilterToList(string dirPath, ref List<FileInfo> filesList, DateTime minDate)
{
    // I'm assuming you want to clear this here... I would generally return it 
    //   instead of passing it as ref
    filesList.Clear();

    // Return all files in directory tree
    foreach (string filePath in Directory.GetFiles(dirPath, "*.*", SearchOption.AllDirectories))
    {
        FileInfo fileInfo = new FileInfo(filePath);
        if (fileInfo.LastWriteTimeUtc > minDate)
        {
            filesList.Add(fileInfo);
        }
    }
}

【讨论】:

  • 这就是我害怕的。这是我以前一直在做的;使用 AllDirectories 参数调用 GetFiles 对所有子目录中的所有文件进行迭代极其缓慢,返回一个包含 10,000 个项目的列表,其中可能只有 2 个会通过 DateTime 检查。
  • 您可以做的一件事是检查目录上的 LastWriteTime,然后仅搜索这些目录中的文件。我不确定这是否会明显更快,但至少可以尝试一下。
  • Aldo,我通过 ref 传递,所以我可以修改一个集合指针,而不是在每次递归时添加/重新创建,但通常,是的,我更喜欢遵循函数式编程:返回一个值和不更改任何参数。
【解决方案2】:

感谢您的所有帮助!

总结:因此,由于DirectoryInfo 对象上没有属性可以显示文件树中任何后代的最后写入时间(仅适用于子文件),因此让 CLR 返回集合使用所有后代文件似乎很容易修复

Directory.GetFiles(dirPath, "*.*", SearchOption.AllDirectories);

但是,在我的情况下,这有一些性能弱点:

  1. 此进程从后端 Web 处理程序运行,因此性能很重要,因为浏览器正在等待写入目录结果
  2. 所有文件都由GetFiles 返回,让您在之后过滤它们。就我而言,这是一个巨大的集合,我只能从中收集到少数最近更改的文件
  3. 我之前没有提到这一点,但我也有一组我不想搜索的路径,我在实际代码中传递了这些路径——在遍历子文件夹之前检查路径可以提高性能对我来说

这是我现在拥有的:

private void AddAllFilesOfAllSubdirsWithFilterToList(ref List<FileInfo> filesList, string dirPath, DateTime startDateUtc, DateTime? optionalEndDateUtc = null, List<string> blockedDirs = null)
{
    // Input validation
    if (String.IsNullOrEmpty(dirPath))
    {
        throw new ArgumentException("Cannot search and empty path");
    }

    DirectoryInfo currentDir = new DirectoryInfo(dirPath);
    if (!currentDir.Exists)
    {
        throw new DirectoryNotFoundException(dirPath + " does not exist");
    }
    if (filesList == null)
    {
        filesList = new List<FileInfo>();
    }

    // Set endDate; add an hour to be safe
    DateTime endDateUtc = optionalEndDateUtc ?? DateTime.UtcNow.AddHours(1);

    // The current folder's LastWriteTime DOES update every time a child FILE is written to,
    // so if the current folder does not pass the date filter, we already know that no files within will pass, either
    if (currentDir.LastWriteTimeUtc >= startDateUtc && currentDir.LastWriteTimeUtc <= endDateUtc)
    {
        foreach (string filePath in Directory.GetFiles(dirPath, "*.*", SearchOption.TopDirectoryOnly))
        {
            FileInfo fileInfo = new FileInfo(filePath);
            if (fileInfo.LastWriteTimeUtc > _sinceDate)
            {
                filesList.Add(fileInfo);
            }
        }
    }

    // Unfortunately, the current folder's LastWriteTime does NOT update every time a child FOLDER is written to,
    // so we have to search ALL subdirectories regardless of the current folder's LastWriteTime
    foreach (string subDirPath in Directory.GetDirectories(dirPath))
    {
        if (blockedDirs == null || !blockedDirs.Any(p => subDirPath.ToLower().Contains(p)))
        {
            AddAllFilesOfAllSubdirsWithFilterToList(ref filesList, subDirPath, startDateUtc, optionalEndDateUtc, blockedDirs);
        }
    }
}

【讨论】:

    【解决方案3】:

    不必遍历目录树。 CLR 非常乐意为您做这件事。

    public FileInfo[] RecentlyWrittenFilesWithin( string path , string searchPattern , DateTime dateFrom , DateTime dateThru )
    {
      if ( string.IsNullOrWhiteSpace( path ) ) {  throw new ArgumentException("invalid path" , "path" );}
      DirectoryInfo root = new DirectoryInfo(path) ;
      if ( !root.Exists ) {  throw new ArgumentException( "non-existent directory" , "path" ) ; }
      bool isDirectory = FileAttributes.Directory == ( FileAttributes.Directory & root.Attributes ) ;
      if ( isDirectory ) {  throw new ArgumentException("not a directory","path");}
    
      FileInfo[] files = root.EnumerateFiles( searchPattern , SearchOption.AllDirectories )
                             .Where( fi => fi.LastWriteTime >= dateFrom && fi.LastWriteTime <= dateThru )
                             .ToArray()
                             ;
      return files ;
    }
    

    根据这里的上下文(例如,如果你的程序是某种服务),你可以在你的根目录上建立一个FileSystemWatcher 并监控发生的变化。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-06
      • 2016-06-07
      • 1970-01-01
      相关资源
      最近更新 更多