diff --git a/installer/MauiProgram.cs b/installer/MauiProgram.cs index 357d6942..f29d0d2e 100755 --- a/installer/MauiProgram.cs +++ b/installer/MauiProgram.cs @@ -1,31 +1,69 @@ using Microsoft.Extensions.Logging; +using System.Reflection; +using CommunityToolkit.Maui; +using CommunityToolkit.Maui.Core; +using CommunityToolkit.Maui.Storage; +using installer.ViewModel; +using System.Runtime.CompilerServices; +using installer.Model; namespace installer { public static class MauiProgram { - public static Model.Downloader Downloader = new Model.Downloader(); + // public static Model.Logger logger = Model.LoggerProvider.FromFile(@"E:\bin\log\123.log"); public static bool ErrorTrigger_WhileDebug = true; public static bool RefreshLogs_WhileDebug = false; + public static string SecretID = "***"; + public static string SecretKey = "***"; public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp() + .UseMauiCommunityToolkitCore() + .UseMauiCommunityToolkit() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); }); - // 此处填写Secret ID和Secret Key - Downloader.Cloud.UpdateSecret("***", "***"); + var c = builder.Services.AddSingleton().First(); + builder.Services.AddSingleton(FolderPicker.Default); + + + AddViewModelService(builder); + AddPageService(builder); #if DEBUG builder.Logging.AddDebug(); #endif return builder.Build(); } + + public static void AddViewModelService(MauiAppBuilder builder) + { + var a = typeof(MauiProgram).Assembly; + foreach (var t in a.GetTypes()) + { + if ((t.FullName ?? string.Empty).StartsWith($"{a.GetName().Name}.ViewModel") && !t.IsAbstract) + { + builder.Services.AddSingleton(t); + } + } + } + public static void AddPageService(MauiAppBuilder builder) + { + var a = typeof(MauiProgram).Assembly; + foreach (var t in a.GetTypes()) + { + if ((t.FullName ?? string.Empty).StartsWith($"{a.GetName().Name}.Page") && !t.IsAbstract) + { + builder.Services.AddSingleton(t); + } + } + } } } \ No newline at end of file diff --git a/installer/Model/Downloader.cs b/installer/Model/Downloader.cs index 3bc0dc1b..1ffd3ee2 100755 --- a/installer/Model/Downloader.cs +++ b/installer/Model/Downloader.cs @@ -128,6 +128,7 @@ public Downloader() Password = temp; } } + Cloud.UpdateSecret(MauiProgram.SecretID, MauiProgram.SecretKey); } public void LoggerBinding() diff --git a/installer/Model/Local_Data.cs b/installer/Model/Local_Data.cs index 34d076ea..2b3d147a 100755 --- a/installer/Model/Local_Data.cs +++ b/installer/Model/Local_Data.cs @@ -25,10 +25,10 @@ public Dictionary Config { get; protected set; } = new Dictionary(); - public Dictionary MD5Data + public ConcurrentDictionary MD5Data { get; protected set; - } = new Dictionary(); // 路径为尽可能相对路径 + } = new ConcurrentDictionary(); // 路径为尽可能相对路径 public ConcurrentBag<(DataRowState state, string name)> MD5Update { get; set; @@ -249,7 +249,7 @@ public void ReadMD5Data() } else { - newMD5Data = JsonSerializer.Deserialize>(json); + newMD5Data = JsonSerializer.Deserialize>(json) ?? new Dictionary(); } r.Close(); r.Dispose(); } @@ -270,19 +270,16 @@ public void ReadMD5Data() foreach (var item in newMD5Data) { var key = item.Key.Replace('/', Path.DirectorySeparatorChar); - if (MD5Data.ContainsKey(key)) + MD5Data.AddOrUpdate(key, (k) => { - if (MD5Data[key] != item.Value) - { - MD5Data[key] = item.Value; - MD5Update.Add((DataRowState.Modified, key)); - } - } - else - { - MD5Data.Add(key, item.Value); MD5Update.Add((DataRowState.Added, key)); - } + return item.Value; + }, (k, v) => + { + if (v != item.Value) + MD5Update.Add((DataRowState.Modified, key)); + return item.Value; + }); } } @@ -314,41 +311,54 @@ public void ScanDir() continue; var file = _file.StartsWith('.') ? Path.Combine(InstallPath, _file) : _file; - if (!File.Exists(file)) + if (!File.Exists(file) && MD5Data.TryRemove(_file, out _)) { - MD5Data.Remove(_file); MD5Update.Add((DataRowState.Deleted, _file)); } } - ScanDir(InstallPath); - SaveMD5Data(); - } - - public void ScanDir(string dir) - { - var d = new DirectoryInfo(dir); - foreach (var file in d.GetFiles()) + // 层序遍历文件树 + Stack stack = new Stack(); + List files = new List(); + stack.Push(InstallPath); + while (stack.Count > 0) { - var relFile = Helper.ConvertAbsToRel(InstallPath, file.FullName); - // 用户自己的文件不会被计入更新hash数据中 - if (IsUserFile(relFile)) - continue; - var hash = Helper.GetFileMd5Hash(file.FullName); - if (MD5Data.Keys.Contains(relFile)) + string cur = stack.Pop(); + files.AddRange(from f in Directory.GetFiles(cur) + where !IsUserFile(f) + select f); + foreach (var d in Directory.GetDirectories(cur)) + stack.Push(d); + } + if (files.Count == 0) + { + MD5Data.Clear(); + SaveMD5Data(); + return; + } + // 并行计算hash值 + var partitioner = Partitioner.Create(0, files.Count); + Parallel.ForEach(partitioner, (range, loopState) => + { + for (int i = range.Item1; i < range.Item2; i++) { - if (MD5Data[relFile] != hash) + if (loopState.IsStopped) + break; + var file = files[i]; + var relFile = Helper.ConvertAbsToRel(InstallPath, file); + var hash = Helper.GetFileMd5Hash(file); + MD5Data.AddOrUpdate(relFile, (k) => { - MD5Data[relFile] = hash; - MD5Update.Add((DataRowState.Modified, relFile)); - } - } - else - { - MD5Data.Add(relFile, hash); - MD5Update.Add((DataRowState.Added, relFile)); + MD5Update.Add((DataRowState.Added, relFile)); + return hash; + }, (k, v) => + { + if (v != hash) + MD5Update.Add((DataRowState.Modified, relFile)); + return hash; + }); } - } - foreach (var d1 in d.GetDirectories()) { ScanDir(d1.FullName); } + }); + SaveMD5Data(); } } } diff --git a/installer/Model/Tencent_Cos.cs b/installer/Model/Tencent_Cos.cs index ee210b50..32c506e2 100755 --- a/installer/Model/Tencent_Cos.cs +++ b/installer/Model/Tencent_Cos.cs @@ -7,6 +7,7 @@ using System.Formats.Tar; using COSXML.Common; using COSXML.Transfer; +using System; // 禁用对没有调用异步API的异步函数的警告 #pragma warning disable CS1998 @@ -38,6 +39,8 @@ public Tencent_Cos(string appid, string region, string bucketName, Logger? _log .SetRegion(Region) // 设置一个默认的存储桶地域 .SetDebugLog(true) // 显示日志 .Build(); // 创建 CosXmlConfig 对象 + QCloudCredentialProvider cosCredentialProvider = new DefaultQCloudCredentialProvider("***", "***", 1000); + cosXml = new CosXmlServer(config, cosCredentialProvider); } public void UpdateSecret(string secretId, string secretKey, long durationSecond = 1000) @@ -93,30 +96,34 @@ public async Task DownloadQueueAsync(string basePath, IEnumerable q { int thID = Log.StartNew(); Log.LogInfo(thID, "Batch download task started."); - int count = queue.Count(); + var array = queue.ToArray(); + int count = array.Count(); int finished = 0; - foreach (var item in queue) + var partitionar = Partitioner.Create(0, count); + Parallel.ForEach(partitionar, (range, loopState) => { - string local = Path.Combine(basePath, item); - int subID = -1; - ThreadPool.QueueUserWorkItem(_ => + for (int i = range.Item1; i < range.Item2; i++) { + if (!loopState.IsStopped) + break; + string local = Path.Combine(basePath, array[i]); + int subID = -1; try { - subID = DownloadFileAsync(local, item).Result; + subID = DownloadFileAsync(local, array[i]).Result; } catch (Exception ex) { - downloadFailed.Enqueue(item); + downloadFailed.Enqueue(array[i]); + Exceptions.Push(ex); } finally { Interlocked.Increment(ref finished); Log.LogInfo(thID, $"Child process: {subID} finished."); } - }); - } - while (finished < count) ; + } + }); Log.LogInfo(thID, "Batch download task finished."); return thID; } diff --git a/installer/Page/InstallPage.xaml b/installer/Page/InstallPage.xaml index 984f9896..da9a5d81 100644 --- a/installer/Page/InstallPage.xaml +++ b/installer/Page/InstallPage.xaml @@ -4,9 +4,6 @@ xmlns:viewModels="clr-namespace:installer.ViewModel" x:Class="installer.Page.InstallPage" Title="Installer"> - - - - - -