MD5Tools/Form1.cs
Dong a92d6a4070 feat: 集成阿里云OSS SDK实现文件下载功能
- 添加阿里云OSS SDK依赖,用于从OSS下载文件
- 重构文件下载逻辑,使用OSS SDK替代原有HTTP下载方式
- 优化MD5文件比较和文件校验流程,提高下载效率
2025-05-12 01:54:57 +08:00

356 lines
13 KiB
C#

using System;
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;
using System.Net.Http;
using System.Security.Cryptography;
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 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<string, (byte[] Data, string ExpectedMd5)> _downloadedFiles = new Dictionary<string, (byte[], string)>();
// 已完成的下载数量
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;
}
private void UpdateProgressValue(int percentage)
{
if (percentage < 0) percentage = 0;
if (percentage > 100) percentage = 100;
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
Update_Pro.Value = Math.Min(Math.Max(percentage, Update_Pro.Minimum), Update_Pro.Maximum);
Application.DoEvents();
});
}
else
{
Update_Pro.Value = Math.Min(Math.Max(percentage, Update_Pro.Minimum), Update_Pro.Maximum);
Application.DoEvents();
}
}
private void UpdateStatus(string message)
{
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 = 4;
int currentStep = 0;
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);
UpdateStatus("比较本地和在线MD5文件...");
var compareResult = CompareMd5Data(onlineData.Data);
if (compareResult.Count == 0)
{
UpdateStatus("所有文件都是最新的,无需更新");
UpdateProgressValue(100);
await Task.Delay(2000);
this.Close();
return;
}
UpdateProgressValue(++currentStep * 100 / totalSteps);
UpdateStatus("下载并验证文件...");
_totalCount = compareResult.Count;
await DownloadAndVerifyFiles(compareResult);
UpdateProgressValue(100);
UpdateStatus("更新完成");
await Task.Delay(1000);
this.Close();
}
catch (Exception ex)
{
UpdateStatus($"更新失败: {ex.Message}");
await Task.Delay(3000);
this.Close();
}
}
private (string Version, string Md5, JObject Data) ReadOnlineMd5File(OssClient client, string bucket, string key)
{
try
{
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 OssException || ex is JsonException)
{
UpdateStatus($"读取在线MD5文件失败: {ex.Message}");
return (null, null, null);
}
}
private bool ValidateOnlineData(string version, string md5, JObject data)
{
if (string.IsNullOrEmpty(version) || string.IsNullOrEmpty(md5) || data == null)
{
UpdateStatus("在线MD5文件无效");
return false;
}
return true;
}
private Dictionary<string, string> CompareMd5Data(JObject onlineData, string currentPath = "")
{
var differences = new Dictionary<string, string>();
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)
{
string expectedMd5 = onlineValue.ToString();
if (!File.Exists(fullPath))
{
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)
{
var subDifferences = CompareMd5Data((JObject)onlineValue, fullPath);
foreach (var diff in subDifferences)
{
if (!differences.ContainsKey(diff.Key))
{
differences[diff.Key] = diff.Value;
}
}
}
}
return differences;
}
private async Task DownloadAndVerifyFiles(Dictionary<string, string> fileList)
{
try
{
OssClient client = new OssClient(OssEndpoint, OssAccessKeyId, OssAccessKeySecret);
_totalCount = fileList.Count;
_completedCount = 0;
_downloadedFiles.Clear();
var options = new ParallelOptions { MaxDegreeOfParallelism = 4 };
await Task.Run(() =>
{
Parallel.ForEach(fileList, options, (file, state) =>
{
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}");
}
VerifyAndSaveAllFiles();
}
catch (Exception ex)
{
UpdateStatus($"下载过程出错: {ex.Message}");
throw;
}
}
private void VerifyAndSaveAllFiles()
{
UpdateStatus("正在校验文件...");
var failedFiles = new List<string>();
int processedCount = 0;
int totalFiles = _downloadedFiles.Count;
foreach (var item in _downloadedFiles)
{
string relativePath = item.Key;
byte[] data = item.Value.Data;
string expectedMd5 = item.Value.ExpectedMd5;
try
{
processedCount++;
UpdateStatus($"正在校验和保存文件 ({processedCount}/{totalFiles}): {relativePath}");
string actualMd5 = CalculateMD5(data);
if (actualMd5 != expectedMd5.ToLower())
{
throw new Exception($"MD5校验失败 (期望: {expectedMd5}, 实际: {actualMd5})");
}
string localPath = Path.Combine(Application.StartupPath, relativePath);
Directory.CreateDirectory(Path.GetDirectoryName(localPath));
File.WriteAllBytes(localPath, data);
int percentage = 95 + (int)(processedCount * 5.0 / totalFiles);
UpdateProgressValue(Math.Min(100, percentage));
}
catch (Exception ex)
{
UpdateStatus($"文件校验失败: {relativePath} - {ex.Message}");
failedFiles.Add(relativePath);
}
}
foreach (var failedFile in failedFiles)
{
_downloadedFiles.Remove(failedFile);
}
if (failedFiles.Count > 0)
{
throw new Exception($"{failedFiles.Count}个文件校验失败");
}
else
{
UpdateStatus("所有文件校验和保存成功");
UpdateProgressValue(100);
}
}
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();
}
}
private string CalculateMD5(byte[] data)
{
using (var md5 = MD5.Create())
{
byte[] hashBytes = md5.ComputeHash(data);
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
}
}
}
}