NewLife/Stardust

星尘代理下载安装dotNet运行时的时候,增加哈希校验,自动从下载站获取文件哈希值,避免本地存在下载了半截的错误文件,然后永远无法恢复正常。最近安装.net10就出现了这个问题。
智能大石头 authored at 2025-11-18 00:41:34
98dca31
Tree
1 Parent(s) 0adb6d6
Summary: 2 changed files with 75 additions and 13 deletions.
Modified +69 -13
Modified +6 -0
Modified +69 -13
diff --git a/Stardust/Managers/NetRuntime.cs b/Stardust/Managers/NetRuntime.cs
index efe6809..efd5afc 100644
--- a/Stardust/Managers/NetRuntime.cs
+++ b/Stardust/Managers/NetRuntime.cs
@@ -7,6 +7,7 @@ using System.Security.Cryptography;
 using NewLife;
 using NewLife.Log;
 using NewLife.Remoting.Clients;
+using NewLife.Web;
 
 namespace Stardust.Managers;
 
@@ -31,6 +32,8 @@ public class NetRuntime
 
     /// <summary>事件客户端</summary>
     public IEventProvider? EventProvider { get; set; }
+
+    private HashSet<String> _urls = new();
     #endregion
 
     #region 构造
@@ -57,25 +60,56 @@ public class NetRuntime
         var fullFile = fileName;
         if (!String.IsNullOrEmpty(CachePath)) fullFile = Path.Combine(CachePath, fileName);
 
-        var hash = "";
-        if (Hashs == null || !Hashs.TryGetValue(fileName, out hash)) hash = null;
+        // 版本相对地址为空,或者主地址已经包含版本相对地址,则直接使用主地址,否则拼接基地址
+        if (String.IsNullOrEmpty(baseUrl) ||
+            !String.IsNullOrEmpty(BaseUrl) && (BaseUrl.EndsWith(baseUrl) || BaseUrl.EndsWith(baseUrl + "/")))
+            baseUrl = BaseUrl?.TrimEnd('/');
+        else
+            baseUrl = BaseUrl?.TrimEnd('/') + '/' + baseUrl?.TrimStart('/').TrimEnd('/');
+
+        // 从页面清单初始化哈希表
+        if (!baseUrl.IsNullOrEmpty() && !_urls.Contains(baseUrl))
+        {
+            var dic = Hashs ??= new Dictionary<String, String>();
+            try
+            {
+                var client = new WebClientX();
+                var links = client.GetLinks(baseUrl);
+
+                foreach (var link in links)
+                {
+                    if (!link.FullName.IsNullOrEmpty() && !link.Hash.IsNullOrEmpty())
+                        dic[link.FullName] = link.Hash;
+                }
+            }
+            catch { }
+
+            _urls.Add(baseUrl);
+        }
+
+        if (Hashs == null || !Hashs.TryGetValue(fileName, out var hash)) hash = null;
 
         // 检查已存在文件的MD5哈希,不正确则重新下载
         var fi = new FileInfo(fullFile);
-        if (fi.Exists && fi.Length < 1024 && !String.IsNullOrEmpty(hash) && GetMD5(fullFile) != hash)
+        if (fi.Exists)
         {
-            fi.Delete();
-            fi = null;
+            if (fi.Length < 1024)
+            {
+                fi.Delete();
+                fi = null;
+            }
+            else if (!String.IsNullOrEmpty(hash))
+            {
+                var hash2 = hash.Length > 32 ? GetSHA512(fullFile) : GetMD5(fullFile);
+                if (!hash2.EqualIgnoreCase(hash))
+                {
+                    fi.Delete();
+                    fi = null;
+                }
+            }
         }
         if (fi == null || !fi.Exists)
         {
-            // 版本相对地址为空,或者主地址已经包含版本相对地址,则直接使用主地址,否则拼接基地址
-            if (String.IsNullOrEmpty(baseUrl) ||
-                !String.IsNullOrEmpty(BaseUrl) && (BaseUrl.EndsWith(baseUrl) || BaseUrl.EndsWith(baseUrl + "/")))
-                baseUrl = BaseUrl?.TrimEnd('/');
-            else
-                baseUrl = BaseUrl?.TrimEnd('/') + '/' + baseUrl.TrimStart('/').TrimEnd('/');
-
             var url = $"{baseUrl}/{fileName}";
             WriteLog("正在下载:{0}", url);
 
@@ -95,7 +129,11 @@ public class NetRuntime
                 http.DownloadFile(url, fullFile);
 #endif
             }
-            WriteLog("MD5: {0}", GetMD5(fullFile));
+            var hash2 = "";
+            if (!String.IsNullOrEmpty(hash))
+                hash2 = hash.Length > 32 ? GetSHA512(fullFile) : GetMD5(fullFile);
+
+            WriteLog("哈希: {0}", hash2);
 
             //// 在windows系统上,下载完成以后,等待一会再安装,避免文件被占用(可能是安全扫描),提高安装成功率
             //if (Runtime.Windows) Thread.Sleep(15_000);
@@ -802,6 +840,8 @@ public class NetRuntime
     /// <returns></returns>
     public static String GetMD5(String fileName)
     {
+        if (!File.Exists(fileName)) return String.Empty;
+
         var fi = new FileInfo(fileName);
         var md5 = MD5.Create();
         using var fs = fi.OpenRead();
@@ -811,6 +851,22 @@ public class NetRuntime
         return hex;
     }
 
+    /// <summary>获取文件SHA512</summary>
+    /// <param name="fileName"></param>
+    /// <returns></returns>
+    public static String GetSHA512(String fileName)
+    {
+        if (!File.Exists(fileName)) return String.Empty;
+
+        var fi = new FileInfo(fileName);
+        var sha = SHA512.Create();
+        using var fs = fi.OpenRead();
+        var buf = sha.ComputeHash(fs);
+        var hex = BitConverter.ToString(buf).Replace("-", null);
+
+        return hex;
+    }
+
     /// <summary>加载内嵌的文件MD5信息</summary>
     /// <returns></returns>
     public static IDictionary<String, String> LoadMD5s()
Modified +6 -0
diff --git a/Test/Program.cs b/Test/Program.cs
index fbb5094..526ad44 100644
--- a/Test/Program.cs
+++ b/Test/Program.cs
@@ -15,6 +15,7 @@ using NewLife.Reflection;
 using NewLife.Remoting;
 using NewLife.Serialization;
 using Stardust;
+using Stardust.Managers;
 using Stardust.Models;
 using Stardust.Windows;
 
@@ -113,6 +114,11 @@ class Program
 
     static void Test3()
     {
+        var nr = new NetRuntime();
+        nr.BaseUrl = "https://x.newlifex.com/dotNet/10.0.0";
+
+        nr.Download("aspnetcore-runtime-10.0.0-linux-x64.tar.gz");
+
         //latencyScore = Math.Exp(-0.01 * (latency - threshold)); // 衰减系数λ=0.2
 
         var ds = new[] { 0.1f, 0.2f, 0.5f, 1f, 2f, 3f, 5f, 10f, 15f, 20f, 30f, 50f, 100f, 200f, 500f, 1000f, 2000f, 5000f };