如何在C#控制台应用中显示文件夹解压与移动的进度条
实现C#控制台的解压与文件夹移动进度显示
一、Zip解压进度实现
ZipFile.ExtractToDirectory 本身没有内置的进度回调,想要显示解压进度,需要通过 ZipArchive 手动遍历压缩包条目,逐文件/逐字节处理并统计进度。
实现思路
- 先计算压缩包内所有文件的总大小(跳过目录条目)
- 遍历每个压缩条目,逐个解压文件
- 按字节块读取写入,实时累计已解压大小,计算进度百分比并输出到控制台
代码示例
using System.IO.Compression; public static void ExtractZipWithProgress(string zipPath, string extractPath) { Directory.CreateDirectory(extractPath); using var archive = ZipFile.OpenRead(zipPath); // 计算压缩包内所有文件的总大小 long totalSize = archive.Entries.Sum(entry => entry.Length); long extractedSize = 0; foreach (var entry in archive.Entries) { if (string.IsNullOrEmpty(entry.Name)) { // 处理目录条目 Directory.CreateDirectory(Path.Combine(extractPath, entry.FullName)); continue; } string destinationPath = Path.Combine(extractPath, entry.FullName); Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); // 按字节块解压,实时更新进度 using var entryStream = entry.Open(); using var fileStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write); byte[] buffer = new byte[8192]; int bytesRead; while ((bytesRead = entryStream.Read(buffer, 0, buffer.Length)) > 0) { fileStream.Write(buffer, 0, bytesRead); extractedSize += bytesRead; // 控制台实时刷新进度(覆盖当前行) int progress = (int)((double)extractedSize / totalSize * 100); Console.Write($"\r解压进度: {progress}% ({extractedSize}/{totalSize} bytes)"); } } Console.WriteLine("\n解压完成"); }
优化提示
如果压缩包内有大量小文件,进度更新会过于频繁,可以添加阈值(比如每累计1%进度再输出一次),减少控制台IO开销。
二、文件夹移动进度实现
文件夹移动分两种场景:同卷移动和跨卷移动。同卷移动只是修改文件系统目录项,几乎瞬间完成,无需显示进度;跨卷移动本质是复制文件+删除原文件,这个过程可以统计进度。
实现思路
- 判断源目录和目标目录是否在同一磁盘卷
- 同卷直接调用
Directory.Move;跨卷则先遍历所有文件计算总大小 - 逐文件按块复制,累计已复制大小并输出进度
- 复制完成后删除原目录
代码示例
public static void MoveDirectoryWithProgress(string sourceDir, string destDir) { DriveInfo sourceDrive = new DriveInfo(Path.GetPathRoot(sourceDir)); DriveInfo destDrive = new DriveInfo(Path.GetPathRoot(destDir)); if (sourceDrive.Name == destDrive.Name) { // 同卷移动,无进度需求 Directory.Move(sourceDir, destDir); Console.WriteLine("同卷移动完成"); return; } // 跨卷移动:复制+删除 Directory.CreateDirectory(destDir); var allFiles = Directory.GetFiles(sourceDir, "*.*", SearchOption.AllDirectories); long totalSize = allFiles.Sum(filePath => new FileInfo(filePath).Length); long copiedSize = 0; foreach (string sourceFile in allFiles) { string relativePath = Path.GetRelativePath(sourceDir, sourceFile); string destFile = Path.Combine(destDir, relativePath); Directory.CreateDirectory(Path.GetDirectoryName(destFile)); // 按字节块复制,更新进度 using var sourceStream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read); using var destStream = new FileStream(destFile, FileMode.Create, FileAccess.Write); byte[] buffer = new byte[8192]; int bytesRead; while ((bytesRead = sourceStream.Read(buffer, 0, buffer.Length)) > 0) { destStream.Write(buffer, 0, bytesRead); copiedSize += bytesRead; int progress = (int)((double)copiedSize / totalSize * 100); Console.Write($"\r移动进度: {progress}% ({copiedSize}/{totalSize} bytes)"); } } // 复制完成后删除原目录 Directory.Delete(sourceDir, true); Console.WriteLine("\n移动完成"); }
注意事项
- 建议添加异常处理逻辑,比如复制失败时回滚已复制的文件
- 缓冲区大小可根据实际场景调整(8192字节是平衡速度与内存占用的常用值)
内容的提问来源于stack exchange,提问作者Ismail Hmama
相关产品推荐
相关产品推荐

