在C#开发过程中,文件写入是高频操作,当目标磁盘剩余空间不足以容纳待写入的数据时,程序会抛出磁盘满相关的异常,若没有提前做好处理,轻则导致当前写入操作失败,重则引发程序崩溃,甚至造成已写入的部分数据损坏。因此掌握磁盘空间不足场景的优雅处理方式,是提升程序健壮性的重要环节。

磁盘空间不足的常见异常类型
C#中写入文件时磁盘满通常会抛出两类异常,开发者需要先明确异常类型才能针对性处理:
- IOException:这是最常见的磁盘满相关异常,当磁盘剩余空间不足以完成写入操作时,系统会抛出该异常,其HResult属性通常会包含磁盘满的错误码。
- UnauthorizedAccessException:如果磁盘已满且同时没有写入权限,可能会抛出该异常,需要结合具体场景判断是否为磁盘空间问题。
写入前检测磁盘剩余空间
为了避免直接写入时触发异常,我们可以在写入前先检测目标磁盘的剩余空间,判断是否满足写入需求。C#中可以通过DriveInfo类获取磁盘的相关信息。
以下是检测指定路径所在磁盘剩余空间的示例代码:
using System;
using System.IO;
public class DiskHelper
{
/// <summary>
/// 获取目标路径所在磁盘的剩余空间(单位:字节)
/// </summary>
/// <param name="filePath">文件路径</param>
/// <returns>剩余空间字节数</returns>
public static long GetRemainingDiskSpace(string filePath)
{
// 获取文件所在的磁盘盘符
string driveName = Path.GetPathRoot(filePath);
if (string.IsNullOrEmpty(driveName))
{
throw new ArgumentException("无法获取文件路径对应的磁盘信息");
}
DriveInfo driveInfo = new DriveInfo(driveName);
// 判断磁盘是否已准备好,比如U盘未插入的情况
if (!driveInfo.IsReady)
{
throw new IOException("目标磁盘未就绪");
}
return driveInfo.AvailableFreeSpace;
}
}
写入时捕获磁盘满异常
即使提前做了空间检测,也可能存在检测后空间被其他程序占用的场景,因此写入操作时仍然需要捕获异常。我们可以通过判断异常的HResult来确认是否为磁盘满问题。
Windows系统中磁盘满的错误码通常为0x80070070,对应的HResult值为-2147024784,我们可以通过该值判断异常类型:
using System;
using System.IO;
public class FileWriteHelper
{
/// <summary>
/// 写入文件内容,处理磁盘满异常
/// </summary>
/// <param name="filePath">目标文件路径</param>
/// <param name="content">待写入内容</param>
public static void WriteFileWithDiskFullHandle(string filePath, string content)
{
try
{
// 确保目录存在
string directory = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
// 执行写入操作
File.WriteAllText(filePath, content);
}
catch (IOException ex)
{
// 判断是否为磁盘满异常,0x80070070对应的HResult为-2147024784
if (ex.HResult == -2147024784)
{
HandleDiskFullException(filePath);
}
else
{
// 其他IO异常重新抛出
throw;
}
}
}
/// <summary>
/// 处理磁盘满异常的逻辑
/// </summary>
private static void HandleDiskFullException(string filePath)
{
// 这里可以自定义处理逻辑,比如提示用户、切换路径等
Console.WriteLine($"写入文件{filePath}失败,目标磁盘空间不足,请清理磁盘空间后重试");
}
}
优雅处理的可选方案
方案1:写入前预检查+阈值判断
在写入前不仅检测剩余空间,还可以设置一个安全阈值,比如要求剩余空间至少比待写入文件大10%,避免写入过程中空间被其他程序占用导致失败:
public static bool CheckDiskSpaceBeforeWrite(string filePath, long requiredSize, double safeThreshold = 0.1)
{
try
{
long remainingSpace = DiskHelper.GetRemainingDiskSpace(filePath);
// 需要的空间为文件大小加上安全阈值部分
long totalRequired = (long)(requiredSize * (1 + safeThreshold));
return remainingSpace >= totalRequired;
}
catch (Exception ex)
{
Console.WriteLine($"检测磁盘空间失败:{ex.Message}");
return false;
}
}
方案2:异常后重试+切换存储路径
捕获到磁盘满异常后,可以先尝试清理临时文件,或者切换到其他有空间的磁盘路径重新写入:
public static void WriteFileWithRetry(string originalPath, string content, int maxRetry = 2)
{
int retryCount = 0;
string currentPath = originalPath;
while (retryCount <= maxRetry)
{
try
{
File.WriteAllText(currentPath, content);
Console.WriteLine($"文件写入成功,路径:{currentPath}");
return;
}
catch (IOException ex) when (ex.HResult == -2147024784)
{
retryCount++;
if (retryCount > maxRetry)
{
Console.WriteLine("多次重试后仍无法写入,磁盘空间不足");
throw;
}
// 切换到其他磁盘路径重试,比如D盘临时目录
string fileName = Path.GetFileName(originalPath);
currentPath = Path.Combine("D:\Temp", fileName);
Console.WriteLine($"第{retryCount}次重试,切换写入路径为:{currentPath}");
}
}
}
方案3:分块写入降低单次压力
如果要写入的文件体积较大,可以采用分块写入的方式,每写入一部分就检查一次剩余空间,避免一次性写入占用过多空间:
public static void WriteLargeFileByChunk(string filePath, byte[] fileData, int chunkSize = 1024 * 1024)
{
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
long totalWritten = 0;
while (totalWritten < fileData.Length)
{
// 检查剩余空间是否足够写入当前块
long remainingSpace = DiskHelper.GetRemainingDiskSpace(filePath);
int currentChunk = (int)Math.Min(chunkSize, fileData.Length - totalWritten);
if (remainingSpace < currentChunk)
{
throw new IOException("磁盘空间不足,无法继续写入文件", -2147024784);
}
fs.Write(fileData, (int)totalWritten, currentChunk);
totalWritten += currentChunk;
}
}
}
注意事项
- 检测磁盘空间时需要注意磁盘未就绪的场景,比如可移动磁盘被拔出的情况,避免抛出不必要的异常。
- 判断磁盘满异常时,不同系统的错误码可能存在差异,如果是跨平台程序需要做兼容处理。
- 切换存储路径时,需要确保新路径所在的磁盘有写入权限,避免出现权限类异常。
- 不要频繁调用磁盘空间检测接口,避免影响程序性能,可以根据业务场景设置合理的检测频率。