C#处理磁盘满异常 C#写入文件时如何优雅地处理磁盘空间不足

6次阅读

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

C#处理磁盘满异常 C#写入文件时如何优雅地处理磁盘空间不足

捕获 IOException 时必须检查 HResult 而非仅靠异常类型

磁盘满在 Windows 上通常抛出 IOException,但不是所有 IOException 都是空间不足——比如权限拒绝、路径不存在、设备脱机也会触发同一异常类型。只用 catch (IOException) 做统一处理,容易掩盖真实问题。

正确做法是检查 Exception.HResult 值:0x80070070(即十进制 -2147024784)对应 Windows ERROR_DISK_FULL。

  • FileStream 写入失败时,HResult0x80070070 才代表磁盘满
  • .NET Core 5+ 和 .NET 6+ 中也可用 ex.Win32ErrorCode == 112ERROR_DISK_FULL),更语义化
  • 不要依赖 ex.Message 包含“disk full”等字符串——本地化后内容会变,不可靠

DriveInfo 预检只能作为辅助,不能替代写入时的异常处理

有人习惯在写入前调用 DriveInfo.AvailableFreeSpace 判断剩余空间,但这只是快照,无法保证写入瞬间仍足够:其他进程可能正大量写入,或 NTFS 有配额、压缩、稀疏文件等干扰因素。

  • 预检适合做“快速拒绝”,比如用户上传 500MB 文件前提示“磁盘剩余不足”,提升体验
  • 但必须保留写入时的 try/catch,因为 AvailableFreeSpace 和实际可写空间存在天然 gap
  • 注意 DriveInfo 构造可能抛 IOException(如光驱无盘、网络驱动器断开),需单独包裹

写入大文件时,FileStreamBuffering 行为会让磁盘满错误延迟暴露

默认 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.RootDirectoryAvailableFreeSpaceTotalFreeSpaceHResult,方便复现
  • 避免在 catch 块里尝试自动清理(如删临时目录)——权限、误删、并发冲突风险高,交给用户决策更安全

磁盘满的本质是资源竞争,没有银弹。预判、捕获、反馈三者缺一不可,但最不可省的是写入路径上的 try/catch ——哪怕你已经做了所有预防。

text=ZqhQzanResources