C#超1TB网络文件复制:故障后断点续传实现方案咨询
实现大文件网络复制的断点续传方案(C#)
要处理1TB+规模的网络文件复制并支持断点续传,核心是持久化复制状态+增量复制,具体实现步骤如下:
一、设计状态存储结构
需要本地存储复制任务的进度信息,重启后可直接加载恢复。推荐用JSON文件或SQLite(数据量大时更高效),以下是核心状态类:
// 单个文件的复制状态 public class FileCopyStatus { public string SourcePath { get; set; } public string TargetPath { get; set; } public long CopiedBytes { get; set; } public long TotalBytes { get; set; } public CopyStatus Status { get; set; } public string SourceHash { get; set; } // 源文件哈希,用于完整性校验 } // 文件夹创建状态 public class FolderCreateStatus { public string SourcePath { get; set; } public string TargetPath { get; set; } public bool IsCreated { get; set; } } // 整个复制任务的状态 public class CopyTaskStatus { public List<FolderCreateStatus> FolderStatuses { get; set; } = new(); public List<FileCopyStatus> FileStatuses { get; set; } = new(); public DateTime LastUpdated { get; set; } } public enum CopyStatus { NotStarted, InProgress, Completed, Failed }
二、初始化/加载任务状态
启动复制前先检查是否存在历史状态:
public static CopyTaskStatus InitializeOrLoadTask(string p_FilePaths) { // 检查是否有已保存的状态文件 if (File.Exists("CopyTaskStatus.json")) { string json = File.ReadAllText("CopyTaskStatus.json"); return JsonSerializer.Deserialize<CopyTaskStatus>(json); } // 初始化新任务状态 var newTask = new CopyTaskStatus(); // 加载所有文件夹并初始化状态 var directoryLevels = GetAllFolders(p_FilePaths); foreach (var (source, target) in directoryLevels) { newTask.FolderStatuses.Add(new FolderCreateStatus { SourcePath = source, TargetPath = target, IsCreated = false }); } // 加载所有文件并初始化状态(提前计算源文件哈希) var filesLevels = GetAllFiles(p_FilePaths); foreach (var (source, target) in filesLevels) { var sourceFile = new FileInfo(source); newTask.FileStatuses.Add(new FileCopyStatus { SourcePath = source, TargetPath = target, TotalBytes = sourceFile.Length, CopiedBytes = 0, Status = CopyStatus.NotStarted, SourceHash = CalculateFileHash(source) }); } SaveTaskStatus(newTask); return newTask; }
三、文件夹复制的断点处理
跳过已创建的文件夹,仅处理未完成的部分:
public static void ProcessFolders(CopyTaskStatus taskStatus) { foreach (var folderStatus in taskStatus.FolderStatuses) { if (folderStatus.IsCreated) continue; try { // 创建目标文件夹(自动创建父目录) Directory.CreateDirectory(folderStatus.TargetPath); folderStatus.IsCreated = true; SaveTaskStatus(taskStatus); // 持久化状态 } catch (Exception ex) { Console.WriteLine($"创建文件夹失败:{folderStatus.TargetPath},错误:{ex.Message}"); // 可加入重试逻辑,例如重试3次后标记为待处理 Thread.Sleep(3000); } } }
四、文件断点续传核心逻辑
根据状态决定是从头复制还是从断点续传,同时定期保存进度:
public static void ProcessFiles(CopyTaskStatus taskStatus) { foreach (var fileStatus in taskStatus.FileStatuses) { if (fileStatus.Status == CopyStatus.Completed) continue; try { var targetFile = new FileInfo(fileStatus.TargetPath); long startPosition = 0; // 处理已存在的目标文件 if (targetFile.Exists) { if (fileStatus.Status == CopyStatus.InProgress) { // 验证目标文件大小是否与已复制字节数一致(避免中途被修改) if (targetFile.Length == fileStatus.CopiedBytes) { startPosition = fileStatus.CopiedBytes; } else { // 文件异常,删除后从头复制 targetFile.Delete(); fileStatus.CopiedBytes = 0; } } else { // 未开始但文件存在,删除后从头复制 targetFile.Delete(); } } // 更新状态为复制中并保存 fileStatus.Status = CopyStatus.InProgress; SaveTaskStatus(taskStatus); // 执行增量复制 using var sourceStream = new FileStream(fileStatus.SourcePath, FileMode.Open, FileAccess.Read, FileShare.Read); using var targetStream = new FileStream(fileStatus.TargetPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None); sourceStream.Seek(startPosition, SeekOrigin.Begin); targetStream.Seek(startPosition, SeekOrigin.Begin); byte[] buffer = new byte[81920]; // 80KB缓冲区,可根据网络带宽调整 int bytesRead; while ((bytesRead = sourceStream.Read(buffer, 0, buffer.Length)) > 0) { targetStream.Write(buffer, 0, bytesRead); fileStatus.CopiedBytes += bytesRead; // 每复制10MB保存一次进度,避免断电丢失 if (fileStatus.CopiedBytes % (1024 * 1024 * 10) == 0) { SaveTaskStatus(taskStatus); } } // 复制完成后校验哈希 string targetHash = CalculateFileHash(fileStatus.TargetPath); if (targetHash == fileStatus.SourceHash) { fileStatus.Status = CopyStatus.Completed; SaveTaskStatus(taskStatus); } else { fileStatus.Status = CopyStatus.Failed; SaveTaskStatus(taskStatus); Console.WriteLine($"文件校验失败:{fileStatus.TargetPath}"); } } catch (IOException ex) { // 网络中断、服务器故障等异常,保存当前进度 fileStatus.Status = CopyStatus.InProgress; SaveTaskStatus(taskStatus); Console.WriteLine($"复制中断:{fileStatus.SourcePath},错误:{ex.Message}"); Thread.Sleep(5000); // 等待5秒后重试当前文件 } catch (Exception ex) { fileStatus.Status = CopyStatus.Failed; SaveTaskStatus(taskStatus); Console.WriteLine($"复制失败:{fileStatus.SourcePath},错误:{ex.Message}"); } } }
五、辅助方法实现
// 计算文件SHA256哈希 private static string CalculateFileHash(string filePath) { using var sha256 = SHA256.Create(); using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); byte[] hashBytes = sha256.ComputeHash(stream); return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant(); } // 保存任务状态到JSON文件 private static void SaveTaskStatus(CopyTaskStatus status) { status.LastUpdated = DateTime.Now; string json = JsonSerializer.Serialize(status, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText("CopyTaskStatus.json", json); }
六、主方法改造
public static void SubmitDocsToRepository(string p_FilePaths) { var taskStatus = InitializeOrLoadTask(p_FilePaths); ProcessFolders(taskStatus); ProcessFiles(taskStatus); // 任务完成后可选择删除状态文件或保留作为凭证 if (taskStatus.FileStatuses.All(f => f.Status == CopyStatus.Completed)) { File.Delete("CopyTaskStatus.json"); } }
关键注意事项
- 状态持久化频率:避免每写一次字节就保存,推荐按固定大小(如10MB)或时间间隔保存,平衡性能与数据安全性
- 并发控制:如果用多线程复制,需给状态文件读写加锁,避免线程冲突
- 重试策略:针对网络异常设置合理的重试次数和间隔,避免频繁重试浪费资源
- 权限处理:确保对源文件有读权限、目标路径有写权限,捕获权限相关异常并提示用户
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

