捕获 ioexception 时必须检查 hresult 而非仅靠异常类型,因磁盘满在 windows 上抛出的 ioexception 与权限拒绝、路径不存在等异常类型相同;正确做法是判断 hresult 是否为 0x80070070(即 win32errorcode == 112),而非依赖 message 或仅用 catch (ioexception) 统一处理。

捕获 IOException 时必须检查 HResult 而非仅靠异常类型
磁盘满在 Windows 上通常抛出 IOException,但不是所有 IOException 都是空间不足——比如权限拒绝、路径不存在、设备脱机也会触发同一异常类型。只用 catch (IOException) 做统一处理,容易掩盖真实问题。
正确做法是检查 Exception.HResult 值:0x80070070(即十进制 -2147024784)对应 Windows ERROR_DISK_FULL。
-
FileStream写入失败时,HResult为0x80070070才代表磁盘满 - .NET Core 5+ 和 .NET 6+ 中也可用
ex.Win32ErrorCode == 112(ERROR_DISK_FULL),更语义化 - 不要依赖
ex.Message包含“disk full”等字符串——本地化后内容会变,不可靠
DriveInfo 预检只能作为辅助,不能替代写入时的异常处理
有人习惯在写入前调用 DriveInfo.AvailableFreeSpace 判断剩余空间,但这只是快照,无法保证写入瞬间仍足够:其他进程可能正大量写入,或 NTFS 有配额、压缩、稀疏文件等干扰因素。
- 预检适合做“快速拒绝”,比如用户上传 500MB 文件前提示“磁盘剩余不足”,提升体验
- 但必须保留写入时的
try/catch,因为AvailableFreeSpace和实际可写空间存在天然 gap - 注意
DriveInfo构造可能抛IOException(如光驱无盘、网络驱动器断开),需单独包裹
写入大文件时,FileStream 的 Buffering 行为会让磁盘满错误延迟暴露
默认 FileStream 启用内核缓冲和用户缓冲(bufferSize=4096),数据先写入内存缓冲区,真正刷盘可能延后几 KB 到几 MB。这意味着:磁盘满异常可能在 Close()、Dispose() 或显式 Flush() 时才抛出,而非 Write() 调用当时。
- 若需及时感知空间不足,创建
FileStream时传入FileOptions.WriteThrough(绕过系统缓存) +FileOptions.NoBuffering(禁用用户缓冲) - 但注意:
NoBuffering要求偏移和长度均为磁盘扇区对齐(通常是 512 或 4096 字节),否则抛ArgumentException - 更实用的做法是定期
Flush()(例如每写入 1MB 后),把错误暴露提前,避免最后一步崩掉
日志记录和用户反馈要区分“技术原因”和“可操作建议”
直接抛 "Disk full" 给终端用户没用,他们不知道该删什么;而只记 HResult=0x80070070 到日志里,运维查问题又太抽象。
- 用户侧提示应具体:比如“C:\ 磁盘剩余 12MB,当前操作需至少 50MB,请清理临时文件或选择其他位置”
- 日志中同时记录:
DriveInfo.RootDirectory、AvailableFreeSpace、TotalFreeSpace、HResult,方便复现 - 避免在 catch 块里尝试自动清理(如删临时目录)——权限、误删、并发冲突风险高,交给用户决策更安全
磁盘满的本质是资源竞争,没有银弹。预判、捕获、反馈三者缺一不可,但最不可省的是写入路径上的 try/catch ——哪怕你已经做了所有预防。










