动态配置并发数量
This commit is contained in:
@@ -15,4 +15,8 @@
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
<appSettings>
|
||||
<add key="MaxConcurrentDownloads" value="2" />
|
||||
<add key="MaxDownloadRetries" value="2" />
|
||||
</appSettings>
|
||||
</configuration>
|
@@ -125,6 +125,7 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Configuration" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Form1.cs">
|
||||
@@ -176,7 +177,7 @@
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。</ErrorText>
|
||||
<ErrorText>这台计算机上缺少此项目引用的 NuGet 程序包。使用"NuGet 程序包还原"可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('packages\Costura.Fody.6.0.0\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Costura.Fody.6.0.0\build\Costura.Fody.props'))" />
|
||||
<Error Condition="!Exists('packages\Costura.Fody.6.0.0\build\Costura.Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Costura.Fody.6.0.0\build\Costura.Fody.targets'))" />
|
||||
|
105
Form1.cs
105
Form1.cs
@@ -22,6 +22,7 @@ using Newtonsoft.Json.Linq;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Configuration;
|
||||
|
||||
namespace CheckDownload
|
||||
{
|
||||
@@ -57,7 +58,9 @@ namespace CheckDownload
|
||||
// 网络优化: 备用DNS服务列表,提高解析成功率
|
||||
private static readonly List<string> _dnsServers = new List<string> { "223.5.5.5", "119.29.29.29" };
|
||||
// 最大并发下载数量
|
||||
private static readonly int MaxConcurrentDownloads = 2;
|
||||
private static readonly int MaxConcurrentDownloads = int.TryParse(ConfigurationManager.AppSettings["MaxConcurrentDownloads"], out var mcd) ? mcd : 4;
|
||||
// 最大下载重试次数
|
||||
private static readonly int MaxDownloadRetries = int.TryParse(ConfigurationManager.AppSettings["MaxDownloadRetries"], out var mdr) ? mdr : 2;
|
||||
// 用于存储下载的文件数据
|
||||
private Dictionary<string, string> _downloadedFiles = new Dictionary<string, string>();
|
||||
// 已完成的下载数量
|
||||
@@ -202,17 +205,33 @@ namespace CheckDownload
|
||||
}
|
||||
|
||||
UpdateStatus("下载并验证文件...");
|
||||
_totalCount = compareResult.Count;
|
||||
await DownloadAndVerifyFiles(compareResult);
|
||||
UpdateProgressValue(100);
|
||||
// 根据路径长度排序,优先下载小文件/浅层文件,可加其它排序规则
|
||||
var orderedFileList = compareResult.OrderBy(k => k.Key.Length)
|
||||
.ToDictionary(k => k.Key, v => v.Value);
|
||||
|
||||
// 更新成功后清理临时文件夹
|
||||
CleanupTempDirectory();
|
||||
_totalCount = orderedFileList.Count;
|
||||
_completedCount = 0;
|
||||
_downloadedFiles.Clear();
|
||||
var failedFiles = new ConcurrentDictionary<string, string>();
|
||||
|
||||
await PerformDownloads(orderedFileList, failedFiles);
|
||||
|
||||
if (!failedFiles.IsEmpty)
|
||||
{
|
||||
UpdateStatus($"有 {failedFiles.Count} 个文件下载失败,开始重试...");
|
||||
var stillFailing = await RetryFailedFilesAsync(new Dictionary<string, string>(failedFiles));
|
||||
if (stillFailing.Any())
|
||||
{
|
||||
UpdateStatus($"重试后仍有 {stillFailing.Count} 个文件下载失败。");
|
||||
}
|
||||
}
|
||||
|
||||
if (_completedCount == 0 && orderedFileList.Count > 0)
|
||||
{
|
||||
throw new Exception("所有文件下载失败。");
|
||||
}
|
||||
|
||||
// 显示更新完成并等待3秒
|
||||
UpdateStatus("更新完成");
|
||||
await Task.Delay(3000);
|
||||
this.Close();
|
||||
await VerifyAndSaveAllFiles();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -539,7 +558,7 @@ namespace CheckDownload
|
||||
|
||||
UpdateStatus($"使用主域名下载文件...");
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, authUrl);
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, authUrl) { Version = HttpVersion.Version11 };
|
||||
request.Headers.Add("User-Agent", "CheckDownload/1.0");
|
||||
|
||||
using (var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead))
|
||||
@@ -574,7 +593,7 @@ namespace CheckDownload
|
||||
|
||||
UpdateStatus($"使用备用域名下载文件...");
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, authUrl);
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, authUrl) { Version = HttpVersion.Version11 };
|
||||
request.Headers.Add("User-Agent", "CheckDownload/1.0");
|
||||
|
||||
using (var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead))
|
||||
@@ -618,7 +637,7 @@ namespace CheckDownload
|
||||
{
|
||||
string authUrl = GenerateAuthUrl($"http://{OneDriveMainDomain}{OneDrivePath}", fileName);
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, authUrl);
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, authUrl) { Version = HttpVersion.Version11 };
|
||||
request.Headers.Add("User-Agent", "CheckDownload/1.0");
|
||||
|
||||
using (var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead))
|
||||
@@ -647,7 +666,7 @@ namespace CheckDownload
|
||||
{
|
||||
string authUrl = GenerateAuthUrl($"http://{OneDriveBackupDomain}{OneDrivePath}", fileName);
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, authUrl);
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, authUrl) { Version = HttpVersion.Version11 };
|
||||
request.Headers.Add("User-Agent", "CheckDownload/1.0");
|
||||
|
||||
using (var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead))
|
||||
@@ -681,10 +700,9 @@ namespace CheckDownload
|
||||
/// <returns>重试后仍然失败的文件字典</returns>
|
||||
private async Task<Dictionary<string, string>> RetryFailedFilesAsync(Dictionary<string, string> failedFiles)
|
||||
{
|
||||
const int maxRetries = 2;
|
||||
var filesToRetry = new Dictionary<string, string>(failedFiles);
|
||||
|
||||
for (int i = 0; i < maxRetries && filesToRetry.Any(); i++)
|
||||
for (int i = 0; i < MaxDownloadRetries && filesToRetry.Any(); i++)
|
||||
{
|
||||
UpdateStatus($"第 {i + 1} 次重试,剩余 {filesToRetry.Count} 个文件...");
|
||||
var failedThisRound = new ConcurrentDictionary<string, string>();
|
||||
@@ -693,7 +711,7 @@ namespace CheckDownload
|
||||
|
||||
filesToRetry = new Dictionary<string, string>(failedThisRound);
|
||||
|
||||
if (filesToRetry.Any() && i < maxRetries - 1)
|
||||
if (filesToRetry.Any() && i < MaxDownloadRetries - 1)
|
||||
{
|
||||
UpdateStatus($"等待 3 秒后进行下一次重试...");
|
||||
await Task.Delay(3000);
|
||||
@@ -715,7 +733,7 @@ namespace CheckDownload
|
||||
try
|
||||
{
|
||||
var requestUri = $"http://{dnsServer}/resolve?name={domain}&type=1&short=1";
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, requestUri) { Version = HttpVersion.Version11 };
|
||||
request.Headers.Add("User-Agent", "CheckDownload/1.0");
|
||||
request.Headers.Host = dnsServer;
|
||||
|
||||
@@ -747,31 +765,31 @@ namespace CheckDownload
|
||||
private async Task VerifyAndSaveAllFiles()
|
||||
{
|
||||
UpdateStatus("正在校验文件...");
|
||||
var failedFiles = new List<string>();
|
||||
var filesForScripting = new List<(string original, string newFile)>();
|
||||
int processedCount = 0;
|
||||
var failedFiles = new ConcurrentBag<string>();
|
||||
var filesForScripting = new ConcurrentBag<(string original, string newFile)>();
|
||||
|
||||
int totalFiles = _downloadedFiles.Count;
|
||||
int completed = 0;
|
||||
var semaphore = new SemaphoreSlim(Environment.ProcessorCount);
|
||||
|
||||
foreach (var item in _downloadedFiles)
|
||||
var tasks = _downloadedFiles.Select(async item =>
|
||||
{
|
||||
string relativePath = item.Key;
|
||||
string expectedMd5 = item.Value;
|
||||
string tempFilePath = Path.Combine(_tempDirectory, relativePath);
|
||||
|
||||
await semaphore.WaitAsync();
|
||||
try
|
||||
{
|
||||
processedCount++;
|
||||
UpdateStatus($"正在校验和保存文件 ({processedCount}/{totalFiles}): {relativePath}");
|
||||
string relativePath = item.Key;
|
||||
string expectedMd5 = item.Value;
|
||||
string tempFilePath = Path.Combine(_tempDirectory, relativePath);
|
||||
|
||||
string actualMd5 = CalculateMD5FromFile(tempFilePath);
|
||||
if (actualMd5 != expectedMd5.ToLower())
|
||||
{
|
||||
throw new Exception($"MD5校验失败 (期望: {expectedMd5}, 实际: {actualMd5})");
|
||||
failedFiles.Add(relativePath);
|
||||
return;
|
||||
}
|
||||
|
||||
string localPath = Path.Combine(_baseDirectory, relativePath);
|
||||
string localDir = Path.GetDirectoryName(localPath);
|
||||
|
||||
if (!Directory.Exists(localDir))
|
||||
{
|
||||
Directory.CreateDirectory(localDir);
|
||||
@@ -782,31 +800,28 @@ namespace CheckDownload
|
||||
string backupPath = localPath + ".new";
|
||||
File.Move(tempFilePath, backupPath);
|
||||
filesForScripting.Add((localPath, backupPath));
|
||||
UpdateStatus($"文件 {relativePath} 正在被占用,将在程序重启后更新");
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateStatus($"文件 {relativePath} 已更新");
|
||||
}
|
||||
|
||||
int percentage = 95 + (int)(processedCount * 5.0 / totalFiles);
|
||||
UpdateProgressValue(Math.Min(100, percentage));
|
||||
}
|
||||
catch (Exception ex)
|
||||
finally
|
||||
{
|
||||
UpdateStatus($"文件校验失败: {relativePath} - {ex.Message}");
|
||||
failedFiles.Add(relativePath);
|
||||
int done = Interlocked.Increment(ref completed);
|
||||
int percentage = 95 + (int)(done * 5.0 / totalFiles);
|
||||
UpdateProgressValue(Math.Min(100, percentage));
|
||||
semaphore.Release();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
|
||||
if (filesForScripting.Any())
|
||||
{
|
||||
CreateReplaceScriptForAll(filesForScripting);
|
||||
CreateReplaceScriptForAll(filesForScripting.ToList());
|
||||
}
|
||||
|
||||
foreach (var failedFile in failedFiles)
|
||||
foreach (var failed in failedFiles)
|
||||
{
|
||||
_downloadedFiles.Remove(failedFile);
|
||||
_downloadedFiles.Remove(failed);
|
||||
}
|
||||
|
||||
if (failedFiles.Count > 0)
|
||||
@@ -1009,7 +1024,7 @@ namespace CheckDownload
|
||||
{
|
||||
var ipUrl = signedUrl.Replace(signedUri.Host, ip);
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, ipUrl);
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, ipUrl) { Version = HttpVersion.Version11 };
|
||||
request.Headers.Host = signedUri.Host;
|
||||
using (var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead))
|
||||
{
|
||||
|
Reference in New Issue
Block a user