refactor(下载逻辑): 重构文件下载和校验流程

将文件下载和校验逻辑拆分为独立的方法,引入内存缓存机制,避免频繁的磁盘操作。所有文件下载完成后统一校验和保存,提高代码可维护性和执行效率。
This commit is contained in:
Dong 2025-05-07 12:21:30 +08:00
parent 5ec395740f
commit 2a339142ad

132
Form1.cs
View File

@ -33,6 +33,9 @@ namespace CheckDownload
/// <summary>在线MD5文件名从DNS查询获取</summary>
private string _onlineMd5File = "";
// 类级别变量,用于存储下载的文件数据
private Dictionary<string, (byte[] Data, string ExpectedMd5)> _downloadedFiles = new Dictionary<string, (byte[], string)>();
/// <summary>已完成下载的文件数</summary>
private int _completedFiles = 0;
@ -345,7 +348,9 @@ namespace CheckDownload
/// <returns>下载任务</returns>
private async Task DownloadUpdatedFiles(Dictionary<string, string> differences)
{
// 设置进度条最大值为需要更新的文件数量
// 重置下载状态
_completedFiles = 0;
_downloadedFiles.Clear();
Update_Pro.Maximum = differences.Count;
Update_Pro.Value = 0;
@ -361,6 +366,9 @@ namespace CheckDownload
// 等待所有下载任务完成
await Task.WhenAll(downloadTasks);
// 所有文件下载完成后统一校验和保存
VerifyAndSaveAllFiles();
}
/// <summary>
@ -371,56 +379,110 @@ namespace CheckDownload
/// <returns>下载任务</returns>
private async Task DownloadFileWithSemaphore(KeyValuePair<string, string> file, SemaphoreSlim semaphore)
{
// 等待信号量,控制并发数量
await semaphore.WaitAsync();
try
{
// 下载并验证文件
await DownloadAndVerifyFile(file.Key, file.Value);
// 更新进度
UpdateProgress();
}
catch (Exception ex)
{
UpdateStatus($"下载失败: {file.Key} - {ex.Message}");
await DownloadToMemory(file.Key, file.Value);
}
finally
{
// 释放信号量,允许其他下载任务执行
semaphore.Release();
}
}
/// <summary>
/// 下载并验证文件
/// 下载文件到内存并暂存
/// </summary>
/// <param name="relativePath">文件的相对路径</param>
/// <param name="expectedMd5">期望的MD5值</param>
/// <returns>下载任务</returns>
private async Task DownloadAndVerifyFile(string relativePath, string expectedMd5)
private async Task DownloadToMemory(string relativePath, string expectedMd5)
{
// 更新状态显示当前下载进度
UpdateStatus($"正在下载 ({Update_Pro.Value + 1}/{Update_Pro.Maximum}): {relativePath}");
// 获取本地路径和临时文件路径
string localPath = GetLocalPath(relativePath);
string tempPath = $"{localPath}.tmp";
// 确保目录存在
EnsureDirectoryExists(localPath);
// 下载文件到临时路径
using (var client = new WebClient())
try
{
await client.DownloadFileTaskAsync(
new Uri($"{BaseDownloadUrl}File/{relativePath}"),
tempPath);
// 更新状态显示当前下载进度
int current = Interlocked.Increment(ref _completedFiles);
UpdateStatus($"正在下载 ({Update_Pro.Value + 1}/{Update_Pro.Maximum}): {expectedMd5}");
// 下载文件到内存
using (var client = new HttpClient())
{
var fileUrl = $"{BaseDownloadUrl}File/{expectedMd5}";
var response = await client.GetAsync(fileUrl);
response.EnsureSuccessStatusCode();
byte[] fileData = await response.Content.ReadAsByteArrayAsync();
// 存储到内存字典
lock (_downloadedFiles)
{
_downloadedFiles[relativePath] = (fileData, expectedMd5);
}
}
// 更新进度
UpdateProgress();
}
catch (Exception ex)
{
UpdateStatus($"下载失败: {relativePath} - {ex.Message}");
throw;
}
}
/// <summary>
/// 校验并保存所有已下载的文件
/// </summary>
private void VerifyAndSaveAllFiles()
{
UpdateStatus("正在校验文件...");
// 创建失败文件列表
var failedFiles = new List<string>();
foreach (var item in _downloadedFiles)
{
string relativePath = item.Key;
byte[] data = item.Value.Data;
string expectedMd5 = item.Value.ExpectedMd5;
try
{
// 计算内存中数据的MD5
string actualMd5;
using (var md5 = MD5.Create())
{
byte[] hashBytes = md5.ComputeHash(data);
actualMd5 = BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
}
// 校验MD5
if (actualMd5 != expectedMd5.ToLower())
{
throw new Exception($"MD5校验失败 (期望: {expectedMd5}, 实际: {actualMd5})");
}
// 获取本地路径并创建目录
string localPath = GetLocalPath(relativePath);
EnsureDirectoryExists(localPath);
// 保存文件
File.WriteAllBytes(localPath, data);
}
catch (Exception ex)
{
UpdateStatus($"文件校验失败: {relativePath} - {ex.Message}");
failedFiles.Add(relativePath);
}
}
// 验证文件MD5
VerifyFileMd5(tempPath, expectedMd5, relativePath);
// 替换现有文件
ReplaceExistingFile(tempPath, localPath);
// 清理失败的文件记录
foreach (var failedFile in failedFiles)
{
_downloadedFiles.Remove(failedFile);
}
if (failedFiles.Count > 0)
{
throw new Exception($"{failedFiles.Count}个文件校验失败");
}
}
/// <summary>