From 15287382a9f1346ea7767e7085d89664c855e152 Mon Sep 17 00:00:00 2001
From: Dong <1278815766@qq.com>
Date: Mon, 12 May 2025 01:45:24 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E9=9B=86=E6=88=90=E9=98=BF=E9=87=8C?=
=?UTF-8?q?=E4=BA=91OSS=20SDK=E4=BB=A5=E5=AE=9E=E7=8E=B0=E6=96=87=E4=BB=B6?=
=?UTF-8?q?=E4=B8=8B=E8=BD=BD=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
此次提交引入了阿里云OSS SDK,用于从阿里云OSS存储桶中下载文件。主要修改包括:
1. 在packages.config和CheckDownload.csproj中添加了阿里云OSS SDK的依赖。
2. 在Form1.cs中重构了文件下载逻辑,使用阿里云OSS SDK替代原有的HTTP下载方式。
3. 移除了原有的DNS查询和本地HTTP服务器依赖,简化了文件获取流程。
4. 优化了文件校验和保存的逻辑,确保下载文件的完整性和正确性。
---
CheckDownload.csproj | 3 +
Form1.cs | 834 +++++++++----------------------------------
packages.config | 1 +
3 files changed, 168 insertions(+), 670 deletions(-)
diff --git a/CheckDownload.csproj b/CheckDownload.csproj
index fc1e1b8..de6e93e 100644
--- a/CheckDownload.csproj
+++ b/CheckDownload.csproj
@@ -48,6 +48,9 @@
4
+
+ packages\Aliyun.OSS.SDK.2.14.1\lib\net461\Aliyun.OSS.dll
+
packages\DnsClient.1.8.0\lib\net472\DnsClient.dll
diff --git a/Form1.cs b/Form1.cs
index 4e8ab56..1ffd7ef 100644
--- a/Form1.cs
+++ b/Form1.cs
@@ -2,7 +2,9 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
+using System.Diagnostics.CodeAnalysis;
using System.Drawing;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
@@ -12,533 +14,275 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
+using Aliyun.OSS;
+using Aliyun.OSS.Common;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace CheckDownload
{
- ///
- /// 自动更新窗体类,负责检查和下载软件更新
- ///
public partial class Update : Form
{
- /// 本地MD5文件名
- private const string LocalMd5File = "md5.json";
- /// DNS查询域名,用于获取更新信息
- private const string DnsQueryDomain = "test.file.ipoi.cn";
- /// 基础下载URL
- private const string BaseDownloadUrl = "http://localhost:60006/";
- /// 最大并发下载数
- private const int MaxConcurrentDownloads = 5;
-
- /// 在线MD5文件名,从DNS查询获取
- private string _onlineMd5File = "";
-
- // 类级别变量,用于存储下载的文件数据
+ // MD5文件名称
+ private const string Md5File = "md5.json";
+ // 阿里云OSS访问地址
+ private const string OssEndpoint = "oss-cn-hongkong.aliyuncs.com";
+ // 阿里云OSS存储空间名称
+ private const string OssBucketName = "sxho-hk";
+ // 阿里云OSS访问密钥ID
+ private const string OssAccessKeyId = "LTAI5tBc8BgooVrHrzfEeg1q";
+ // 阿里云OSS访问密钥Secret
+ private const string OssAccessKeySecret = "nMvSp0UqzodTzKImuAMK1a1bkSks5O";
+ // 用于存储下载的文件数据
private Dictionary _downloadedFiles = new Dictionary();
- /// 已完成下载的文件数
- private int _completedFiles = 0;
- /// 需要下载的总文件数
- private int _totalFilesToDownload = 0;
+ // 已完成的下载数量
+ private int _completedCount = 0;
+ // 总下载数量
+ private int _totalCount = 0;
- ///
- /// 构造函数,初始化更新窗体
- ///
+ // 初始化窗体
public Update()
{
InitializeComponent();
ConfigureProgressBar();
}
- ///
- /// 配置进度条初始设置
- ///
+ // 配置进度条
private void ConfigureProgressBar()
{
- Update_Pro.Minimum = 0; // 设置最小值
- Update_Pro.Maximum = 100; // 设置最大值
- Update_Pro.Value = 0; // 设置初始值
- Update_Pro.Step = 1; // 设置步进值
+ Update_Pro.Minimum = 0;
+ Update_Pro.Maximum = 100;
+ Update_Pro.Value = 0;
+ Update_Pro.Step = 1;
}
-
- ///
- /// 更新进度条值
- ///
- /// 进度百分比(0-100)
+
private void UpdateProgressValue(int percentage)
{
- // 确保百分比在有效范围内
if (percentage < 0) percentage = 0;
if (percentage > 100) percentage = 100;
-
- // 在UI线程上更新进度条值
+
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
- // 确保进度条值不超出范围
- if (percentage >= Update_Pro.Minimum && percentage <= Update_Pro.Maximum)
- {
- Update_Pro.Value = percentage;
- }
- else
- {
- // 如果超出范围,则设置为最大或最小值
- Update_Pro.Value = Math.Min(Math.Max(percentage, Update_Pro.Minimum), Update_Pro.Maximum);
- }
- // 添加Application.DoEvents()调用,确保消息队列被处理
+ Update_Pro.Value = Math.Min(Math.Max(percentage, Update_Pro.Minimum), Update_Pro.Maximum);
Application.DoEvents();
});
}
else
{
- if (percentage >= Update_Pro.Minimum && percentage <= Update_Pro.Maximum)
- {
- Update_Pro.Value = percentage;
- }
- else
- {
- Update_Pro.Value = Math.Min(Math.Max(percentage, Update_Pro.Minimum), Update_Pro.Maximum);
- }
+ Update_Pro.Value = Math.Min(Math.Max(percentage, Update_Pro.Minimum), Update_Pro.Maximum);
Application.DoEvents();
}
}
- ///
- /// 窗体加载事件处理方法,启动更新流程
- ///
- /// 事件源
- /// 事件参数
- public async void Update_Load(object sender, EventArgs e)
+ private void UpdateStatus(string message)
{
- // 设置窗体位置到屏幕右下角
- PositionFormToBottomRight();
-
- await UpdateFile(); // 执行更新流程
- // 注意:窗体关闭逻辑已移至UpdateFile方法中,确保显示完成消息后再关闭
- }
-
- ///
- /// 设置窗体位置到屏幕右下角
- ///
- private void PositionFormToBottomRight()
- {
- // 获取当前屏幕的工作区域(排除任务栏等系统元素)
- Rectangle workingArea = Screen.GetWorkingArea(this);
-
- // 计算窗体在右下角的位置
- int x = workingArea.Right - this.Width;
- int y = workingArea.Bottom - this.Height;
-
- // 设置窗体位置
- this.Location = new Point(x, y);
+ if (this.InvokeRequired)
+ {
+ this.Invoke((MethodInvoker)delegate { Status_Box.Text = message; });
+ }
+ else
+ {
+ Status_Box.Text = message;
+ }
+ }
+
+ public async void Update_Load(object sender, EventArgs e)
+ {
+ PositionFormToBottomRight();
+ await UpdateFile();
+ }
+
+ private void PositionFormToBottomRight()
+ {
+ Rectangle workingArea = Screen.GetWorkingArea(this);
+ this.Location = new Point(workingArea.Right - this.Width, workingArea.Bottom - this.Height);
}
- ///
- /// 主要更新流程,检查并下载更新文件
- ///
- /// 更新任务
private async Task UpdateFile()
{
try
{
- // 定义更新流程的总步骤数和当前步骤
- const int totalSteps = 6; // 总共6个主要步骤
+ const int totalSteps = 4;
int currentStep = 0;
-
- // 更新进度条初始状态
- UpdateProgressValue(0);
-
- // 步骤1: 读取本地MD5文件信息
- UpdateStatus("读取本地MD5文件...");
- var localData = await ReadLocalMd5File();
- if (!ValidateLocalData(localData.Version, localData.Md5, localData.Data)) return;
- UpdateProgressValue(++currentStep * 100 / totalSteps); // 更新进度为16%
- // 步骤2: 获取在线MD5文件信息
- UpdateStatus("获取在线MD5文件信息...");
- var onlineData = await GetOnlineMd5File();
- if (!ValidateOnlineData(onlineData.Version, onlineData.Md5)) return;
- UpdateProgressValue(++currentStep * 100 / totalSteps); // 更新进度为33%
+ UpdateStatus("下载在线MD5文件并读取...");
+ OssClient client = new OssClient(OssEndpoint, OssAccessKeyId, OssAccessKeySecret);
+ var onlineData = ReadOnlineMd5File(client, OssBucketName, Md5File);
+ if (!ValidateOnlineData(onlineData.Version, onlineData.Md5, onlineData.Data)) return;
+ UpdateProgressValue(++currentStep * 100 / totalSteps);
- // 步骤3: 比较版本,判断是否需要更新
- UpdateStatus("比较版本信息...");
- if (!ShouldUpdate(localData.Version, onlineData.Version))
+ UpdateStatus("比较本地和在线MD5文件...");
+ var compareResult = CompareMd5Data(onlineData.Data);
+ if (compareResult.Count == 0)
{
- UpdateStatus("当前已是最新版本,无需更新");
- UpdateProgressValue(100); // 完成进度
+ UpdateStatus("所有文件都是最新的,无需更新");
+ UpdateProgressValue(100);
+ await Task.Delay(2000);
+ this.Close();
return;
}
- UpdateProgressValue(++currentStep * 100 / totalSteps); // 更新进度为50%
+ UpdateProgressValue(++currentStep * 100 / totalSteps);
- // 步骤4: 下载在线MD5文件
- UpdateStatus("下载在线MD5文件...");
- var onlineFileData = await DownloadOnlineMd5File();
- if (onlineFileData == null) return;
- UpdateProgressValue(++currentStep * 100 / totalSteps); // 更新进度为66%
+ UpdateStatus("下载并验证文件...");
+ _totalCount = compareResult.Count;
+ await DownloadAndVerifyFiles(compareResult);
+ UpdateProgressValue(100);
- // 步骤5: 比较文件差异
- UpdateStatus("比较文件差异...");
- var differences = CompareDataDifferences(localData.Data, onlineFileData);
- UpdateProgressValue(++currentStep * 100 / totalSteps); // 更新进度为83%
-
- // 步骤6: 下载和更新文件
- if (differences.Count > 0)
- {
- // 下载需要更新的文件
- await DownloadUpdatedFiles(differences);
- }
- else
- {
- UpdateStatus("无文件需要更新");
- }
-
- // 替换本地MD5文件
- ReplaceLocalMd5File();
- UpdateProgressValue(100); // 完成进度
-
- // 显示更新完成消息
UpdateStatus("更新完成");
-
- // 等待1秒后关闭窗体
await Task.Delay(1000);
this.Close();
}
catch (Exception ex)
{
- UpdateStatus($"发生错误: {ex.Message}");
+ UpdateStatus($"更新失败: {ex.Message}");
+ await Task.Delay(3000);
+ this.Close();
}
}
- ///
- /// 验证本地数据的有效性
- ///
- /// 版本号
- /// MD5校验值
- /// 文件数据对象
- /// 数据是否有效
- private bool ValidateLocalData(string version, string md5, JObject data)
- {
- if (string.IsNullOrEmpty(version) || string.IsNullOrEmpty(md5) || data == null)
- {
- UpdateStatus("本地MD5文件无效");
- return false;
- }
- return true;
- }
-
- ///
- /// 验证在线数据的有效性
- ///
- /// 版本号
- /// MD5校验值
- /// 数据是否有效
- private bool ValidateOnlineData(string version, string md5)
- {
- if (string.IsNullOrEmpty(version) || string.IsNullOrEmpty(md5))
- {
- UpdateStatus("无法获取在线MD5信息");
- return false;
- }
- return true;
- }
-
- ///
- /// 读取本地MD5文件并解析内容
- ///
- /// 包含版本号、MD5值和数据对象的元组
- private async Task<(string Version, string Md5, JObject Data)> ReadLocalMd5File()
+ private (string Version, string Md5, JObject Data) ReadOnlineMd5File(OssClient client, string bucket, string key)
{
try
{
- UpdateStatus("读取md5.json...");
- // 异步读取文件内容
- string json = await Task.Run(() => File.ReadAllText(LocalMd5File));
- // 解析JSON对象
- var obj = JObject.Parse(json);
- // 获取版本号
- string version = obj["version"]?.ToString();
- // 获取数据对象
- var data = (JObject)obj["data"];
- // 计算JSON内容的MD5值
- string jsonMd5 = CalculateMD5(json);
- return (version, jsonMd5, data);
+ var obj = client.GetObject(bucket, key);
+ using (var reader = new StreamReader(obj.Content))
+ {
+ string json = reader.ReadToEnd();
+ var parsed = JObject.Parse(json);
+ string version = parsed["version"]?.ToString();
+ var data = (JObject)parsed["data"];
+ string jsonMd5 = CalculateMD5(json);
+ return (version, jsonMd5, data);
+ }
}
- catch (Exception ex) when (ex is FileNotFoundException || ex is JsonException)
+ catch (Exception ex) when (ex is OssException || ex is JsonException)
{
- UpdateStatus($"读取本地MD5文件失败: {ex.Message}");
+ UpdateStatus($"读取在线MD5文件失败: {ex.Message}");
return (null, null, null);
}
}
- ///
- /// 获取在线MD5文件信息
- ///
- /// 包含版本号和MD5值的元组
- private async Task<(string Version, string Md5)> GetOnlineMd5File()
+ private bool ValidateOnlineData(string version, string md5, JObject data)
{
- try
+ if (string.IsNullOrEmpty(version) || string.IsNullOrEmpty(md5) || data == null)
{
- UpdateStatus("解析在线md5.json...");
- // 通过DNS查询获取响应数据
- string responseData = await QueryDnsAsync();
- // 反序列化JSON字符串
- string firstUnescaped = JsonConvert.DeserializeObject(responseData);
- // 解析JSON对象
- var dataJson = JObject.Parse(firstUnescaped);
-
- // 获取版本号和MD5值
- string version = dataJson["version"]?.ToString();
- string md5 = dataJson["md5"]?.ToString();
-
- // 设置在线MD5文件名
- _onlineMd5File = $"{md5}.json";
-
- return (version, md5);
- }
- catch (Exception ex)
- {
- UpdateStatus($"获取在线MD5信息失败: {ex.Message}");
- return (null, null);
- }
- }
-
- ///
- /// 判断是否需要更新
- ///
- /// 本地版本号
- /// 在线版本号
- /// 是否需要更新
- private bool ShouldUpdate(string localVersion, string onlineVersion)
- {
- try
- {
- UpdateStatus("校验信息...");
- // 解析版本号字符串为Version对象
- var localVer = new Version(localVersion);
- var onlineVer = new Version(onlineVersion);
- // 比较版本号,如果本地版本小于在线版本,则需要更新
- return localVer.CompareTo(onlineVer) < 0;
- }
- catch (Exception ex) when (ex is FormatException || ex is ArgumentNullException)
- {
- UpdateStatus($"版本比较失败: {ex.Message}");
+ UpdateStatus("在线MD5文件无效");
return false;
}
+ return true;
}
- ///
- /// 下载在线MD5文件并解析内容
- ///
- /// 文件数据对象
- private async Task DownloadOnlineMd5File()
- {
- try
- {
- UpdateStatus("下载在线md5.json文件...");
- // 使用WebClient下载文件
- using (var client = new WebClient())
- {
- await client.DownloadFileTaskAsync(
- new Uri($"{BaseDownloadUrl}{_onlineMd5File}"),
- _onlineMd5File);
- }
-
- UpdateStatus("读取在线md5.json文件...");
- // 异步读取文件内容
- string json = await Task.Run(() => File.ReadAllText(_onlineMd5File));
- // 解析JSON对象
- var obj = JObject.Parse(json);
- // 返回数据部分
- return (JObject)obj["data"];
- }
- catch (Exception ex)
- {
- UpdateStatus($"下载在线MD5文件失败: {ex.Message}");
- return null;
- }
- }
-
- ///
- /// 比较本地数据和在线数据的差异
- ///
- /// 本地数据对象
- /// 在线数据对象
- /// 当前路径,用于递归调用
- /// 需要更新的文件路径和MD5值的字典
- private Dictionary CompareDataDifferences(JObject localData, JObject onlineData, string currentPath = "")
+ private Dictionary CompareMd5Data(JObject onlineData, string currentPath = "")
{
var differences = new Dictionary();
foreach (var onlineProperty in onlineData.Properties())
{
- // 获取属性名称
string key = onlineProperty.Name;
- // 获取属性值
JToken onlineValue = onlineProperty.Value;
- // 构建完整路径
string fullPath = string.IsNullOrEmpty(currentPath) ? key : $"{currentPath}/{key}";
if (onlineValue.Type == JTokenType.String)
{
- // 如果是字符串类型,表示是文件的MD5值
string expectedMd5 = onlineValue.ToString();
- // 判断是否需要下载该文件
- if (ShouldDownloadFile(localData, key, expectedMd5, fullPath))
+ if (!File.Exists(fullPath))
{
- differences[fullPath] = expectedMd5;
+ if (!differences.ContainsKey(fullPath))
+ {
+ differences[fullPath] = expectedMd5;
+ }
+ }
+ else
+ {
+ string localMd5 = CalculateMD5(File.ReadAllBytes(fullPath));
+ if (localMd5 != expectedMd5)
+ {
+ if (!differences.ContainsKey(fullPath))
+ {
+ differences[fullPath] = expectedMd5;
+ }
+ }
}
}
else if (onlineValue.Type == JTokenType.Object)
{
- // 如果是对象类型,表示是目录,递归比较
- JObject localSubData = GetSubData(localData, key);
- var subDifferences = CompareDataDifferences(localSubData, (JObject)onlineValue, fullPath);
+ var subDifferences = CompareMd5Data((JObject)onlineValue, fullPath);
foreach (var diff in subDifferences)
{
- differences[diff.Key] = diff.Value;
+ if (!differences.ContainsKey(diff.Key))
+ {
+ differences[diff.Key] = diff.Value;
+ }
}
}
}
-
return differences;
}
- ///
- /// 判断是否需要下载文件
- ///
- /// 本地数据对象
- /// 文件键名
- /// 期望的MD5值
- /// 文件完整路径
- /// 是否需要下载
- private bool ShouldDownloadFile(JObject localData, string key, string expectedMd5, string fullPath)
- {
- // 本地数据中不存在该键
- bool fileMissing = !localData.ContainsKey(key);
- // 本地数据中存在该键,但MD5值不匹配
- bool md5Mismatch = localData.ContainsKey(key) &&
- (localData[key].Type != JTokenType.String ||
- localData[key].ToString() != expectedMd5);
- // 物理文件不存在
- bool physicalFileMissing = !File.Exists(Path.Combine(".", fullPath.Replace('/', '\\')));
-
- // 满足任一条件则需要下载
- return fileMissing || md5Mismatch || physicalFileMissing;
- }
-
- ///
- /// 获取本地数据中的子数据对象
- ///
- /// 本地数据对象
- /// 子数据键名
- /// 子数据对象,如果不存在则返回空对象
- private JObject GetSubData(JObject localData, string key)
- {
- // 如果本地数据中存在该键且类型为对象,则返回该对象,否则返回空对象
- return localData.ContainsKey(key) && localData[key].Type == JTokenType.Object
- ? (JObject)localData[key]
- : new JObject();
- }
-
- ///
- /// 下载需要更新的文件
- ///
- /// 需要更新的文件路径和MD5值的字典
- /// 下载任务
- private async Task DownloadUpdatedFiles(Dictionary differences)
- {
- // 重置下载状态
- _completedFiles = 0;
- _downloadedFiles.Clear();
-
- // 设置总文件数
- _totalFilesToDownload = differences.Count;
-
- // 更新状态信息
- UpdateStatus($"开始下载更新文件,共{differences.Count}个文件...");
-
- // 创建信号量控制并发下载数量
- var semaphore = new SemaphoreSlim(MaxConcurrentDownloads);
- var downloadTasks = new List();
-
- // 为每个需要更新的文件创建下载任务
- foreach (var file in differences)
- {
- downloadTasks.Add(DownloadFileWithSemaphore(file, semaphore));
- }
-
- // 等待所有下载任务完成
- await Task.WhenAll(downloadTasks);
-
- // 所有文件下载完成后统一校验和保存
- UpdateStatus("所有文件下载完成,开始校验和保存...");
- VerifyAndSaveAllFiles();
- }
-
- ///
- /// 使用信号量控制并发下载文件
- ///
- /// 文件路径和MD5值的键值对
- /// 控制并发的信号量
- /// 下载任务
- private async Task DownloadFileWithSemaphore(KeyValuePair file, SemaphoreSlim semaphore)
- {
- await semaphore.WaitAsync();
- try
- {
- await DownloadToMemory(file.Key, file.Value);
- }
- finally
- {
- semaphore.Release();
- }
- }
-
- ///
- /// 下载文件到内存并暂存
- ///
- private async Task DownloadToMemory(string relativePath, string expectedMd5)
+ private async Task DownloadAndVerifyFiles(Dictionary fileList)
{
try
{
- // 更新状态显示当前下载进度
- int current = Interlocked.Increment(ref _completedFiles);
-
- // 下载文件到内存
- using (var client = new HttpClient())
+ OssClient client = new OssClient(OssEndpoint, OssAccessKeyId, OssAccessKeySecret);
+ _totalCount = fileList.Count;
+ _completedCount = 0;
+ _downloadedFiles.Clear();
+
+ var options = new ParallelOptions { MaxDegreeOfParallelism = 4 };
+
+ await Task.Run(() =>
{
- var fileUrl = $"{BaseDownloadUrl}File/{expectedMd5}";
- var response = await client.GetAsync(fileUrl);
- response.EnsureSuccessStatusCode();
-
- byte[] fileData = await response.Content.ReadAsByteArrayAsync();
-
- // 存储到内存字典
- lock (_downloadedFiles)
+ Parallel.ForEach(fileList, options, (file, state) =>
{
- _downloadedFiles[relativePath] = (fileData, expectedMd5);
- }
+ string filePath = file.Key;
+ string expectedMd5 = file.Value;
+
+ try
+ {
+ string ossKey = $"File/{expectedMd5}";
+ var obj = client.GetObject(OssBucketName, ossKey);
+ using (var memoryStream = new MemoryStream())
+ {
+ obj.Content.CopyTo(memoryStream);
+ byte[] fileData = memoryStream.ToArray();
+
+ lock (_downloadedFiles)
+ {
+ _downloadedFiles[filePath] = (fileData, expectedMd5);
+ }
+
+ Interlocked.Increment(ref _completedCount);
+ int progress = (int)((double)_completedCount / _totalCount * 95);
+ UpdateProgressValue(progress);
+ UpdateStatus($"已下载 {_completedCount}/{_totalCount}");
+ }
+ }
+ catch (Exception ex)
+ {
+ UpdateStatus($"下载文件 {filePath} 失败: {ex.Message}");
+ }
+ });
+ });
+
+ if (_downloadedFiles.Count != fileList.Count)
+ {
+ throw new Exception($"部分文件下载失败,成功 {_downloadedFiles.Count}/{fileList.Count}");
}
- // 更新进度
- UpdateProgress();
+ VerifyAndSaveAllFiles();
}
catch (Exception ex)
{
- UpdateStatus($"下载失败: {relativePath} - {ex.Message}");
+ UpdateStatus($"下载过程出错: {ex.Message}");
throw;
}
}
- ///
- /// 校验并保存所有已下载的文件
- ///
private void VerifyAndSaveAllFiles()
{
UpdateStatus("正在校验文件...");
-
- // 创建失败文件列表
var failedFiles = new List();
int processedCount = 0;
int totalFiles = _downloadedFiles.Count;
@@ -551,40 +295,21 @@ namespace CheckDownload
try
{
- // 更新状态信息
processedCount++;
UpdateStatus($"正在校验和保存文件 ({processedCount}/{totalFiles}): {relativePath}");
-
- // 计算内存中数据的MD5
- string actualMd5;
- using (var md5 = MD5.Create())
- {
- byte[] hashBytes = md5.ComputeHash(data);
- actualMd5 = BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
- }
- // 校验MD5
+ string actualMd5 = CalculateMD5(data);
if (actualMd5 != expectedMd5.ToLower())
{
throw new Exception($"MD5校验失败 (期望: {expectedMd5}, 实际: {actualMd5})");
}
- // 获取本地路径并创建目录
- string localPath = GetLocalPath(relativePath);
- EnsureDirectoryExists(localPath);
-
- // 保存文件
+ string localPath = Path.Combine(Application.StartupPath, relativePath);
+ Directory.CreateDirectory(Path.GetDirectoryName(localPath));
File.WriteAllBytes(localPath, data);
-
- // 更新进度条 - 从95%到98%
- // 确保即使在totalFiles为0的情况下也不会出现除零错误,并且百分比不会超过98
- int percentage = 95;
- if (totalFiles > 0)
- {
- // 计算进度时确保不会超过98%
- percentage = 95 + Math.Min(3, (int)(processedCount * 3.0 / totalFiles));
- }
- UpdateProgressValue(percentage);
+
+ int percentage = 95 + (int)(processedCount * 5.0 / totalFiles);
+ UpdateProgressValue(Math.Min(100, percentage));
}
catch (Exception ex)
{
@@ -593,7 +318,6 @@ namespace CheckDownload
}
}
- // 清理失败的文件记录
foreach (var failedFile in failedFiles)
{
_downloadedFiles.Remove(failedFile);
@@ -606,257 +330,27 @@ namespace CheckDownload
else
{
UpdateStatus("所有文件校验和保存成功");
+ UpdateProgressValue(100);
}
}
- ///
- /// 获取文件的本地路径
- ///
- /// 文件的相对路径
- /// 本地文件路径
- private string GetLocalPath(string relativePath)
- {
- // 将相对路径转换为本地路径,替换斜杠为反斜杠
- return Path.Combine(".", relativePath.Replace('/', '\\'));
- }
-
- ///
- /// 确保文件所在目录存在
- ///
- /// 文件路径
- private void EnsureDirectoryExists(string filePath)
- {
- // 获取文件所在目录
- string directory = Path.GetDirectoryName(filePath);
- // 如果目录不存在,则创建目录
- if (!Directory.Exists(directory))
- {
- Directory.CreateDirectory(directory);
- }
- }
-
- ///
- /// 验证文件的MD5值
- ///
- /// 文件路径
- /// 期望的MD5值
- /// 文件的相对路径,用于错误提示
- /// 当MD5校验失败时抛出异常
- private void VerifyFileMd5(string filePath, string expectedMd5, string relativePath)
- {
- // 计算文件的实际MD5值
- string actualMd5 = CalculateFileMD5(filePath);
- // 如果MD5值不匹配,删除文件并抛出异常
- if (actualMd5 != expectedMd5)
- {
- File.Delete(filePath);
- throw new Exception($"MD5校验失败: {relativePath}");
- }
- }
-
- ///
- /// 替换现有文件
- ///
- /// 临时文件路径
- /// 目标文件路径
- private void ReplaceExistingFile(string tempPath, string localPath)
- {
- // 如果目标文件已存在,则先删除
- if (File.Exists(localPath))
- {
- File.Delete(localPath);
- }
- // 将临时文件移动到目标位置
- File.Move(tempPath, localPath);
- }
-
- ///
- /// 更新文件下载进度
- ///
- private void UpdateProgress()
- {
- // 计算下载进度百分比,避免除零错误
- int percentage = 0;
- if (_totalFilesToDownload > 0)
- {
- percentage = (int)(_completedFiles * 100.0 / _totalFilesToDownload);
- }
-
- // 更新状态信息
- UpdateStatus($"正在下载文件 ({_completedFiles}/{_totalFilesToDownload})...");
-
- // 在UI线程上更新进度条值
- this.BeginInvoke((MethodInvoker)delegate
- {
- // 在下载阶段,进度从83%到95%之间变化
- // 确保不会超过95%
- int overallPercentage = 83 + Math.Min(12, (int)(percentage * 12.0 / 100));
- // 使用UpdateProgressValue方法确保值在有效范围内
- UpdateProgressValue(overallPercentage);
- });
- }
-
- ///
- /// 替换本地MD5文件为在线下载的新版本
- ///
- private void ReplaceLocalMd5File()
- {
- try
- {
- // 如果本地MD5文件已存在,则先删除
- if (File.Exists(LocalMd5File))
- {
- File.Delete(LocalMd5File);
- }
- // 将下载的在线MD5文件移动到本地MD5文件位置
- File.Move(_onlineMd5File, LocalMd5File);
- UpdateStatus("更新完成");
- }
- catch (Exception ex)
- {
- UpdateStatus($"更新本地版本信息失败: {ex.Message}");
- }
- }
-
- ///
- /// 更新状态信息显示
- ///
- /// 要显示的状态信息
- private void UpdateStatus(string message)
- {
- // 使用Invoke而不是BeginInvoke,确保UI更新完成
- if (this.InvokeRequired)
- {
- this.Invoke((MethodInvoker)delegate
- {
- Status_Box.Text = message;
- // 立即刷新控件,确保状态实时显示
- Status_Box.Update();
- // 添加Application.DoEvents()调用,确保消息队列被处理
- Application.DoEvents();
- });
- }
- else
- {
- Status_Box.Text = message;
- Status_Box.Update();
- Application.DoEvents();
- }
- }
-
- ///
- /// 计算字符串的MD5值
- ///
- /// 输入字符串
- /// MD5哈希值(小写十六进制字符串)
private string CalculateMD5(string input)
{
using (var md5 = MD5.Create())
{
- // 将字符串转换为字节数组
byte[] inputBytes = Encoding.UTF8.GetBytes(input);
- // 计算哈希值
byte[] hashBytes = md5.ComputeHash(inputBytes);
- // 将字节数组转换为十六进制字符串并返回
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
}
}
- ///
- /// 计算文件的MD5值
- ///
- /// 文件路径
- /// MD5哈希值(小写十六进制字符串)
- private string CalculateFileMD5(string filePath)
+ private string CalculateMD5(byte[] data)
{
using (var md5 = MD5.Create())
- using (var stream = File.OpenRead(filePath))
{
- // 计算文件流的哈希值
- byte[] hashBytes = md5.ComputeHash(stream);
- // 将字节数组转换为十六进制字符串并返回
+ byte[] hashBytes = md5.ComputeHash(data);
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
}
}
-
- ///
- /// 通过多个DNS over HTTPS服务器查询TXT记录
- ///
- /// 查询到的TXT记录内容
- private static async Task QueryDnsAsync()
- {
- // DNS over HTTPS服务器列表
- var dohServers = new List
- {
- "https://cloudflare-dns.com/dns-query",
- "https://dns.cloudflare.com/dns-query",
- "https://1.1.1.1/dns-query",
- "https://1.0.0.1/dns-query",
- "https://dns.google/resolve",
- "https://sm2.doh.pub/dns-query",
- "https://doh.pub/dns-query",
- "https://dns.alidns.com/resolve",
- "https://223.5.5.5/resolve",
- "https://223.6.6.6/resolve",
- "https://doh.360.cn/resolve"
- };
-
- using (var httpClient = new HttpClient())
- {
- // 创建取消令牌源,用于取消其他任务
- var cts = new CancellationTokenSource();
- var tasks = new List>();
-
- // 并行查询所有DNS服务器
- foreach (var server in dohServers)
- {
- tasks.Add(QueryDnsServer(httpClient, server, cts.Token));
- }
-
- // 等待任意一个任务完成
- var completedTask = await Task.WhenAny(tasks);
- // 取消其他正在进行的任务
- cts.Cancel();
- // 返回第一个完成的任务结果
- return await completedTask;
- }
- }
-
- ///
- /// 查询指定DNS over HTTPS服务器的TXT记录
- ///
- /// HTTP客户端
- /// DNS服务器URL
- /// 取消令牌
- /// 查询到的TXT记录内容,失败则返回空字符串
- private static async Task QueryDnsServer(HttpClient client, string server, CancellationToken token)
- {
- try
- {
- // 构建DNS查询URL
- var url = $"{server}?name={DnsQueryDomain}&type=TXT";
- // 发送HTTP请求获取响应
- var response = await client.GetStringAsync(url);
- // 解析JSON响应
- var jsonResponse = JObject.Parse(response);
-
- // 遍历Answer部分查找TXT记录
- foreach (var record in jsonResponse["Answer"])
- {
- string txtRecord = record["data"]?.ToString();
- // 如果找到有效的TXT记录,则返回
- if (!string.IsNullOrEmpty(txtRecord))
- {
- return txtRecord;
- }
- }
- return string.Empty;
- }
- catch
- {
- // 查询失败时返回空字符串
- return string.Empty;
- }
- }
}
}
\ No newline at end of file
diff --git a/packages.config b/packages.config
index 1763c0c..86450b3 100644
--- a/packages.config
+++ b/packages.config
@@ -1,5 +1,6 @@
+