improve:自动更新

This commit is contained in:
2025-07-22 18:45:43 +08:00
parent 7565f624c9
commit be770d4607
3 changed files with 80 additions and 56 deletions
+2 -2
View File
@@ -49,5 +49,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.7.1.10")] [assembly: AssemblyVersion("1.7.1.11")]
[assembly: AssemblyFileVersion("1.7.1.10")] [assembly: AssemblyFileVersion("1.7.1.11")]
+52 -28
View File
@@ -87,6 +87,15 @@ namespace Ink_Canvas.Helpers
} }
}; };
// 区块任务结构体(移到类体内)
private class BlockTask
{
public int Index;
public long Start;
public long End;
public int RetryCount;
}
// 检测URL延迟 // 检测URL延迟
private static async Task<long> GetUrlDelay(string url) private static async Task<long> GetUrlDelay(string url)
{ {
@@ -519,8 +528,7 @@ namespace Ink_Canvas.Helpers
{ {
LogHelper.WriteLogToFile($"AutoUpdate | 正在尝试多线程下载: {fileUrl}"); LogHelper.WriteLogToFile($"AutoUpdate | 正在尝试多线程下载: {fileUrl}");
int maxRetry = 3; int maxRetry = 3;
int threadCount = 32; // 提升线程数至32 int threadCount = 32;
// 1. 获取文件总大小
long totalSize = await GetContentLength(fileUrl); long totalSize = await GetContentLength(fileUrl);
if (totalSize <= 0) if (totalSize <= 0)
{ {
@@ -528,29 +536,39 @@ namespace Ink_Canvas.Helpers
return false; return false;
} }
int blockSize = (int)Math.Ceiling((double)totalSize / threadCount); int blockSize = (int)Math.Ceiling((double)totalSize / threadCount);
long[] blockDownloaded = new long[threadCount]; int blockCount = (int)Math.Ceiling((double)totalSize / blockSize);
var tasks = new List<Task>();
CancellationTokenSource cts = new CancellationTokenSource(); // 这里不再定义BlockTask类,直接使用
for (int i = 0; i < threadCount; i++) var blockQueue = new System.Collections.Concurrent.ConcurrentQueue<BlockTask>();
var finishedBlocks = new System.Collections.Concurrent.ConcurrentDictionary<int, bool>();
long[] blockDownloaded = new long[blockCount];
for (int i = 0; i < blockCount; i++)
{ {
int blockIndex = i; long start = i * blockSize;
long start = blockIndex * blockSize;
long end = Math.Min(start + blockSize - 1, totalSize - 1); long end = Math.Min(start + blockSize - 1, totalSize - 1);
string tempPath = destinationPath + $".part{blockIndex}"; blockQueue.Enqueue(new BlockTask { Index = i, Start = start, End = end, RetryCount = 0 });
}
CancellationTokenSource cts = new CancellationTokenSource();
var tasks = new List<Task>();
for (int t = 0; t < threadCount; t++)
{
tasks.Add(Task.Run(async () => tasks.Add(Task.Run(async () =>
{ {
int retryCount = 0; while (blockQueue.TryDequeue(out var block))
while (retryCount < maxRetry) {
bool success = false;
for (int retry = block.RetryCount; retry < maxRetry && !success; retry++)
{ {
try try
{ {
using (var client = new HttpClient()) using (var client = new HttpClient())
{ {
var req = new HttpRequestMessage(HttpMethod.Get, fileUrl); var req = new HttpRequestMessage(HttpMethod.Get, fileUrl);
req.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(start, end); req.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(block.Start, block.End);
using (var resp = await client.SendAsync(req, HttpCompletionOption.ResponseHeadersRead, cts.Token)) using (var resp = await client.SendAsync(req, HttpCompletionOption.ResponseHeadersRead, cts.Token))
{ {
resp.EnsureSuccessStatusCode(); resp.EnsureSuccessStatusCode();
string tempPath = destinationPath + $".part{block.Index}";
using (var fs = new FileStream(tempPath, FileMode.Create, FileAccess.Write, FileShare.None)) using (var fs = new FileStream(tempPath, FileMode.Create, FileAccess.Write, FileShare.None))
{ {
var stream = await resp.Content.ReadAsStreamAsync(); var stream = await resp.Content.ReadAsStreamAsync();
@@ -559,7 +577,7 @@ namespace Ink_Canvas.Helpers
while ((read = await stream.ReadAsync(buffer, 0, buffer.Length, cts.Token)) > 0) while ((read = await stream.ReadAsync(buffer, 0, buffer.Length, cts.Token)) > 0)
{ {
await fs.WriteAsync(buffer, 0, read, cts.Token); await fs.WriteAsync(buffer, 0, read, cts.Token);
blockDownloaded[blockIndex] += read; blockDownloaded[block.Index] += read;
// 合并所有块进度 // 合并所有块进度
long totalDownloaded = blockDownloaded.Sum(); long totalDownloaded = blockDownloaded.Sum();
double percent = (double)totalDownloaded / totalSize * 100; double percent = (double)totalDownloaded / totalSize * 100;
@@ -568,44 +586,50 @@ namespace Ink_Canvas.Helpers
} }
} }
} }
break; // 成功则退出重试 success = true;
} }
catch (Exception ex) when (ex is HttpRequestException || ex is IOException) catch (Exception ex) when (ex is HttpRequestException || ex is IOException)
{ {
retryCount++; LogHelper.WriteLogToFile($"AutoUpdate | 分块{block.Index}下载失败,第{retry + 1}次: {ex.Message}", LogHelper.LogType.Warning);
if (retryCount >= maxRetry) progressCallback?.Invoke(0, $"分块{block.Index}下载失败,第{retry + 1}次: {ex.Message}");
await Task.Delay(15000);
}
}
if (success)
{ {
LogHelper.WriteLogToFile($"AutoUpdate | 分块{blockIndex}下载失败,已重试{retryCount}次: {ex.Message}", LogHelper.LogType.Error); finishedBlocks[block.Index] = true;
progressCallback?.Invoke(0, $"分块{blockIndex}下载失败,已重试{retryCount}次: {ex.Message}"); }
cts.Cancel(); else if (block.RetryCount + 1 < maxRetry)
break; {
// 失败但未超最大重试,重新入队
block.RetryCount++;
blockQueue.Enqueue(block);
} }
else else
{ {
LogHelper.WriteLogToFile($"AutoUpdate | 分块{blockIndex}网络异常,{15 * retryCount}s后第{retryCount}次重试: {ex.Message}", LogHelper.LogType.Warning); // 超过最大重试,取消所有任务
progressCallback?.Invoke(0, $"分块{blockIndex}网络异常,{15 * retryCount}s后第{retryCount}次重试..."); cts.Cancel();
await Task.Delay(15000); break;
}
} }
} }
})); }));
} }
await Task.WhenAll(tasks); await Task.WhenAll(tasks);
if (cts.IsCancellationRequested) if (cts.IsCancellationRequested || finishedBlocks.Count != blockCount)
{ {
progressCallback?.Invoke(0, "多线程下载失败"); progressCallback?.Invoke(0, "多线程下载失败");
// 清理分块文件 // 清理分块文件
for (int i = 0; i < threadCount; i++) for (int i = 0; i < blockCount; i++)
{ {
string tempPath = destinationPath + $".part{i}"; string tempPath = destinationPath + $".part{i}";
if (File.Exists(tempPath)) File.Delete(tempPath); if (File.Exists(tempPath)) File.Delete(tempPath);
} }
return false; return false;
} }
// 3. 合并所有块 // 合并所有块
using (var output = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None)) using (var output = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None))
{ {
for (int i = 0; i < threadCount; i++) for (int i = 0; i < blockCount; i++)
{ {
string tempPath = destinationPath + $".part{i}"; string tempPath = destinationPath + $".part{i}";
using (var input = new FileStream(tempPath, FileMode.Open, FileAccess.Read)) using (var input = new FileStream(tempPath, FileMode.Open, FileAccess.Read))
+2 -2
View File
@@ -49,5 +49,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.7.1.9")] [assembly: AssemblyVersion("1.7.1.11")]
[assembly: AssemblyFileVersion("1.7.1.9")] [assembly: AssemblyFileVersion("1.7.1.11")]