C#目录大小计算 C#如何递归计算一个文件夹占用的总空间

1次阅读

Directory.GetFileSystemEntries 会漏掉隐藏 / 系统文件,应改用 Directory.EnumerateFileSystemEntries 手动判断属性;FileInfo.Length 返回逻辑大小而非磁盘占用,需 Win32 API 获取真实字节数;递归遍历须用迭代避免栈溢出;符号链接、UNC 路径和系统目录需特殊处理。

C# 目录大小计算 C# 如何递归计算一个文件夹占用的总空间

Directory.GetFileSystemEntries 会漏掉隐藏 / 系统文件

直接用 Directory.GetFiles 默认不返回隐藏或系统属性的文件,导致计算结果偏小。尤其在 Windows 系统盘或用户目录下,pagefile.syshiberfil.sys 或 OneDrive 隐藏同步元数据都可能被跳过。

正确做法是显式传入 SearchOption.AllDirectoriesSearchOption.TopDirectoryOnly 组合,并用 FileAttributes.AllAttributes 过滤时排除 FileAttributes.ExcludeArchive 类逻辑——但更稳妥的是绕过过滤,改用 Directory.EnumerateFileSystemEntries + 手动判断:

foreach (var entry in Directory.EnumerateFileSystemEntries(root, "*", SearchOption.AllDirectories)) {if ((File.GetAttributes(entry) & FileAttributes.ReparsePoint) == 0) // 跳过符号链接和挂载点     {if (File.Exists(entry))         {try { size += new FileInfo(entry).Length; }             catch (UnauthorizedAccessException) {/* 忽略权限不足的文件 */}             catch (IOException) {/* 忽略正在使用的文件(如 Outlook PST)*/}         }     } }

FileInfo.Length 返回的是逻辑大小,不是磁盘占用

一个 1KB 的文本文件在 NTFS 上实际可能占 4KB 簇空间;而稀疏文件或压缩文件(NTFS 压缩、WIM 挂载)的 FileInfo.Length 和磁盘实际写入字节数完全不同。

若需真实磁盘占用,必须调用 Win32 API:GetCompressedFileSize(兼容压缩 / 稀疏)或 GetDiskFreeSpaceEx 配合 FindFirstFile 获取 dwFileSizeHigh/low —— 但 C# 中最简方案是用 Microsoft.VisualBasic.FileIO.FileSystem.GetDriveInfo 的间接方式,或直接 P/Invoke:

[DllImport("kernel32.dll")] static extern uint GetCompressedFileSize(string lpFileName, out uint lpFileSizeHigh); // 注意:返回值为低 32 位,lpFileSizeHigh 输出高 32 位,需组合为 long

多数场景下用 Length 已足够(比如用户空间配额估算),但做备份校验或磁盘分析时,这个差异会导致 5%~30% 的误差。

递归遍历大目录时容易触发 StackOverflowException

纯递归(如自己写 CalculateSize(string path) 并对每个子目录调用自身)在深度 > 1000 层的路径结构中极易崩溃,且无法捕获。

应改用基于栈或队列的迭代遍历:

  • Stack<string></string> 存待处理目录,每次 Pop() 后扫描其子项,把子目录 Push() 进栈
  • 避免深递归,也便于加取消令牌(CancellationToken)或进度回调
  • 注意:Windows 路径最大长度为 260,启用长路径支持(App.config<runtime><enforceFIPolicy enabled="false"/></runtime>)后仍需用 ? 前缀才能突破限制

符号链接、挂载点、网络驱动器需特殊处理

Directory.EnumerateDirectories 默认会跟随符号链接(ReparsePoint),造成重复计数甚至无限循环(如 A → B,B → A)。而网络驱动器(Z:)在断开时抛 DirectoryNotFoundException,但 servershare UNC 路径可能返回空枚举而不报错。

关键检查点:

  • File.GetAttributes(path) & FileAttributes.ReparsePoint 判断是否为链接,再读 FileSystemEnumerableIterator<T> 底层或调用 DeviceIoControl 区分类型
  • 对 UNC 路径,先用 Path.IsUncPath 判断,再用 NetworkInterface.GetIsNetworkAvailable() 做粗略连通性预检
  • 跳过 $RECYCLE.BINSystem Volume Information 等系统保护目录——它们即使有权限也可能返回 0 字节或拒绝访问

真正难的不是算数,而是决定「哪些该算、哪些该跳、哪些算不准却得假装知道」——尤其是跨平台部署时,Linux/macOS 的硬链接、ACL、扩展属性会让同一套逻辑行为完全不同。

星耀云
版权声明:本站原创文章,由 星耀云 2026-03-20发表,共计2188字。
转载说明:转载本网站任何内容,请按照转载方式正确书写本站原文地址。本站提供的一切软件、教程和内容信息仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。
text=ZqhQzanResources