我试图计算文件夹大小,但问题是,它对D:\驱动器或其他文件夹工作得很快,但是每当我试图单击C:\驱动器时,应用程序就会冻结一段时间,大约7-8秒。(我的驱动器列表在treeview上)当我删除文件夹大小时,一切正常。你们知道这件事吗?
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;
}
}发布于 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异常)和可读性:
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异常时,枚举将被中止。此版本继续使用递归枚举所有剩余的目录:
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
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
}
}使用
// 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。
https://stackoverflow.com/questions/70168553
复制相似问题