首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >计算文件夹大小/枚举文件系统

计算文件夹大小/枚举文件系统
EN

Stack Overflow用户
提问于 2021-11-30 11:24:13
回答 1查看 368关注 0票数 1

我试图计算文件夹大小,但问题是,它对D:\驱动器或其他文件夹工作得很快,但是每当我试图单击C:\驱动器时,应用程序就会冻结一段时间,大约7-8秒。(我的驱动器列表在treeview上)当我删除文件夹大小时,一切正常。你们知道这件事吗?

代码语言:javascript
复制
   public FolderModel(string folderPath)
    {
        try
        {

            //File = new FileInfo(folderPath);
            //FolderInfo = new DirectoryInfo(folderPath);
            //_createdTime = FolderInfo.CreationTime.ToShortDateString();
            //_folderName = FolderInfo.Name;
            //_folderPath = folderPath;
            //Fileextension = File.Extension.ToLower();
            //this.Children = new ObservableCollection<FolderModel>();

            _folderSize = CalculatorSize(GetDirectorySize(folderPath));
           
        }
        catch (Exception e)
        {
            //
        }
    }




    internal string CalculatorSize(long bytes)
    {
        var suffix = new[] { "B", "KB", "MB", "GB", "TB" };
        float byteNumber = bytes;
        for (var i = 0; i < suffix.Length; i++)
        {
            if (byteNumber < 1000)
            {
                if (i == 0)
                    return $"{byteNumber} {suffix[i]}";
                else
                    return $"{byteNumber:0.#0} {suffix[i]}";
            }
            else
            {
                byteNumber /= 1024;
            }
        }
        return $"{byteNumber:N} {suffix[suffix.Length - 1]}";
    }



    internal static long GetDirectorySize(string directoryPath)
    {
        try
        {
            if (Directory.Exists(directoryPath))
            {
                var d = new DirectoryInfo(directoryPath);
                return d.EnumerateFiles("*", SearchOption.AllDirectories).Sum(fi => fi.Length);
            }

            return new FileInfo(directoryPath).Length;
        }
        catch (UnauthorizedAccessException)
        {
            return 0;
        }
        catch (FileNotFoundException)
        {
            return 0;
        }
        catch (DirectoryNotFoundException)
        {
            return 0;
        }
    }
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-11-30 14:06:26

必须枚举后台线程上的文件夹。

提高性能的建议

当使用DriveInfo API时,您可以进一步提高文件夹路径是驱动器的情况下的性能。在这种情况下,您可以省略完整驱动器的枚举,这通常需要一段时间。

此外,当前实现在枚举抛出UnauthorizedAccessException异常时中止计算。你不会想要那样的。您希望算法忽略禁止的文件系统路径。

以下两个示例展示了您的实现的一个固定的和改进的版本。

第一个解决方案的目标是现代.NET标准2.1兼容的.NET版本。

第二种解决方案的目标是旧的.NET框架。

.NET标准2.1 (.NET Core3.0,.NET 5)

当使用与.NET标准2.1兼容的.NET版本(如.NET Core3.0和.NET 5)时,您可以消除异常处理。使用EnumerationOptions作为参数允许API忽略不可访问的目录,这大大提高了性能(不再有UnauthorizedAccessException异常)和可读性:

代码语言:javascript
复制
internal static async Task<bool> TryGetDirectorySize(string directoryPath, out long spaceUsedInBytes)
{
  spaceUsedInBytes = -1;
  var drives = DriveInfo.GetDrives();
  DriveInfo targetDrive = drives.FirstOrDefault(drive => drive.Name.Equals(directoryPath, StringComparison.OrdinalIgnoreCase));

  // Directory is a drive: skip the expensive enumeration of complete drive.
  if (targetDrive != null)
  {
    spaceUsedInBytes = targetDrive.TotalSize - targetDrive.TotalFreeSpace;
    return true;
  }

  if (!Directory.Exists(folderPath))
  {
    return false;
  }

  // Consider to make this local variable a private property
  var enumerationOptions = new EnumerationOptions { RecurseSubdirectories = true };

  var targetFolderInfo = new DirectoryInfo(directoryPath);
  spaceUsedInBytes = await Task.Run(
    () => targetFolderInfo.EnumerateFiles("*", enumerationOptions)
      .Sum(fileInfo => fileInfo.Length));

  return true;
}

.NET框架

符合.NET框架的版本。它解决了原始代码中的问题,当抛出UnauthorizedAccessException异常时,枚举将被中止。此版本继续使用递归枚举所有剩余的目录:

代码语言:javascript
复制
internal static async Task<long> GetDirectorySize(string directoryPath)
{
  long spaceUsedInBytes = -1;
  var drives = DriveInfo.GetDrives();
  DriveInfo targetDrive =  drives.FirstOrDefault(drive => drive.Name.Equals(directoryPath, StringComparison.OrdinalIgnoreCase));

  // Directory is a drive: skip enumeration of complete drive.
  if (targetDrive != null)
  {
    spaceUsedInBytes = targetDrive.TotalSize - targetDrive.TotalFreeSpace;
    return spaceUsedInBytes;
  }

  var targetDirectoryInfo = new DirectoryInfo(directoryPath);
  spaceUsedInBytes = await Task.Run(() => SumDirectorySize(targetDirectoryInfo));
  return spaceUsedInBytes;
}

private static long SumDirectorySize(DirectoryInfo parentDirectoryInfo)
{
  long spaceUsedInBytes = 0;
  try
  {
    spaceUsedInBytes = parentDirectoryInfo.EnumerateFiles("*", SearchOption.TopDirectoryOnly)
      .Sum(fileInfo => fileInfo.Length);
  }
  catch (UnauthorizedAccessException)
  {
    return 0;
  }

  foreach (var subdirectoryInfo in parentDirectoryInfo.EnumerateDirectories("*", SearchOption.TopDirectoryOnly))
  {
    spaceUsedInBytes += SumDirectorySize(subdirectoryInfo);
  }

  return spaceUsedInBytes;
}

如何实例化需要在构造中运行异步操作的类型

FolderModel.cs

代码语言:javascript
复制
class FolderModel
{
  // Make a constructor private to force instantiation using the factory method
  private FolderModel(string folderPath)
  {
    // Do non-async initialization
  }

  // Async factory method: add constructor parameters to async factory method
  public static async Task<FolderModel> CreateAsync(string folderPath)
  {
    var instance = new FolderModel(folderPath);
    await instance.InitializeAsync(folderPath);
    return instance;
  }

  // Define member as protected virtual to allow derived classes to add initialization routines
  protected virtual async Task InitializeAsync(string directoryPath)
  {
    // Consider to throw an exception here ONLY in case the folder is generated programmatically.
    // If folder is retrieved from user input, use input validation 
    // or even better use a folder picker dialog
    // to ensure that the provided path is always valid!
    if (!Directory.Exists(directoryPath))
    {
      throw new DirectoryNotFoundException($"Invalid directory path '{directoryPath}'.");
    }

    long folderSize = await GetDirectorySize(directoryPath);

    // TODO::Do something with the 'folderSize' value 
    // and execute other async code if necessary
  }
}

使用

代码语言:javascript
复制
// Create an instance of FolderModel example
private async Task SomeMethod()
{
  // Always await async methods (methods that return a Task).
  // Call static CreateAsync method instead of the constructor.
  FolderModel folderModel = await FolderModel.CreateAsync(@"C:\");
}

在更高级的场景中,如果您想推迟初始化(例如,因为您希望避免分配现在或永远不需要的昂贵资源),则可以在引用依赖于这些资源的某个成员时使实例调用InitializeAsync,或者可以将构造函数和InitializeAsync方法公之于众,以允许类的用户显式调用InitializeAsync

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70168553

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档