Ver código fonte

FileChecker、DeserializeJson

龙腾猫跃 4 meses atrás
pai
commit
9470a6160a

+ 4 - 4
Shared/Extensions/ExceptionExtensions.cs

@@ -31,9 +31,9 @@ public static class ExceptionExtensions {
 
     /// <summary>
     /// 获取 <paramref name="ex"/> 的用户友好描述。
-    /// 若 <paramref name="showMultilineStacks"/> 为 true,则返回多行的详细描述与堆栈信息;否则不整理堆栈,仅将 <see cref="Exception.Message"/> 汇总到一行。
+    /// 若 <paramref name="multiline"/> 为 true,则返回多行的详细描述与堆栈信息;否则不整理堆栈,仅将 <see cref="Exception.Message"/> 汇总到一行。
     /// </summary>
-    public static string GetDisplay(this Exception? ex, bool showMultilineStacks) {
+    public static string GetDisplay(this Exception? ex, bool multiline) {
         if (ex is null) return "无可用错误信息!";
 
         // 提取堆栈信息
@@ -45,7 +45,7 @@ public static class ExceptionExtensions {
             _ => ""
         });
         foreach (var currentEx in ex.Flatten()) {
-            if (showMultilineStacks) {
+            if (multiline) {
                 lines.Add((isInnerException ? "→ " : "") + getExceptionMessage(currentEx).ReplaceLineEndings("\r\n", true));
                 var stackLines = (currentEx.StackTrace?.SplitLines(true) ?? [])
                     .Select(l => l.BeforeLast("(") + l.AfterLast(")"))
@@ -73,7 +73,7 @@ public static class ExceptionExtensions {
             commonReason = "你的网络环境不佳,请稍后再试,或使用 VPN 改善网络环境。";
 
         // 输出
-        if (showMultilineStacks) {
+        if (multiline) {
             if (commonReason is null) {
                 return lines.Join("\r\n");
             } else {

+ 19 - 5
Shared/Extensions/StringExtensions.cs

@@ -1,4 +1,6 @@
-using System.Text.RegularExpressions;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System.Text.RegularExpressions;
 
 namespace MeloongCore.Extensions;
 public static class StringExtensions {
@@ -331,8 +333,6 @@ public static class StringExtensions {
 
     #endregion
 
-    #region 读取为字符串
-
     /// <summary>
     /// 读取 <see cref="Stream"/> 中的内容,并解码为字符串。
     /// 会将流的位置主动重置到开头。
@@ -366,8 +366,6 @@ public static class StringExtensions {
         }
     }
 
-    #endregion
-
     /// <summary>
     /// 将第一个字符转换为大写,其余字符转换为小写。
     /// </summary>
@@ -400,4 +398,20 @@ public static class StringExtensions {
         return result ^ 0xA98F501BC684032FUL;
     }
 
+    /// <summary>
+    /// 将字符串转化为 JSON 对象。
+    /// </summary>
+    public static JToken? DeserializeJson(this string data) {
+        try {
+            return JsonConvert.DeserializeObject<JToken>(data, new JsonSerializerSettings {DateTimeZoneHandling = DateTimeZoneHandling.Local});
+        } catch (Exception ex) {
+            int length = (data ?? "").Length;
+            throw new FormatException("JSON deserialize failed: " + 
+                (length > 2000
+                    ? $"{data![..500]}...(total {length} characters)...{data[^500..]}"
+                    : (data ?? "")), 
+            ex);
+        }
+    }
+
 }

+ 6 - 0
Shared/MeloongCore.csproj

@@ -21,6 +21,12 @@
 		</PackageReference>
 	</ItemGroup>
 
+	<ItemGroup>
+	  <Reference Include="Newtonsoft.Json">
+	    <HintPath>..\..\Plain Craft Launcher 2\Resources\Newtonsoft.Json.dll</HintPath>
+	  </Reference>
+	</ItemGroup>
+
 	<ItemGroup>
 		<Using Include="System.Diagnostics.CodeAnalysis" />
 		<Using Include="System.Collections" />

+ 84 - 0
Shared/Utils/FileUtils.cs

@@ -406,3 +406,87 @@ public static class FileUtils {
         => new(PathUtils.ForApi(path));
 
 }
+
+public class FileChecker {
+
+    /// <summary>
+    /// 文件的准确大小。
+    /// <para/>不检查则为 -1。
+    /// </summary>
+    public long ActualSize = -1;
+
+    /// <summary>
+    /// 文件的最小大小。
+    /// <para/>不检查则为 -1。
+    /// </summary>
+    public long MinSize = -1;
+
+    /// <summary>
+    /// 文件的 MD5、SHA1、SHA256 或 SHA512。会根据输入字符串的长度自动判断种类。
+    /// <para/>不检查则为 <c>null</c>。
+    /// </summary>
+    public string? Hash = null;
+
+    /// <summary>
+    /// 是否要求为 JSON 文件。
+    /// </summary>
+    public bool IsJson = false;
+
+    /// <summary>
+    /// 检查文件。
+    /// <para/>若成功则返回 <c>null</c>,失败则返回错误的描述文本(文本不以句号结尾)。
+    /// <para/>不会抛出异常。
+    /// </summary>
+    public string? Check(string localPath) {
+        try {
+            var info = FileUtils.GetInfo(localPath);
+            if (!info.Exists) return "文件不存在:" + localPath;
+
+            long fileSize = info.Length;
+            if (ActualSize >= 0 && ActualSize != fileSize) {
+                return $"文件大小应为 {ActualSize} B,实际为 {fileSize} B" +
+                    (fileSize < 2000 ? ",内容为:" + FileUtils.ReadAsString(localPath) : "");
+            }
+            if (MinSize >= 0 && MinSize > fileSize) {
+                return $"文件大小应至少为 {MinSize} B,实际为 {fileSize} B" +
+                    (fileSize < 2000 ? ",内容为:" + FileUtils.ReadAsString(localPath) : "");
+            }
+
+            string? hash = Hash;
+            if (!string.IsNullOrEmpty(hash)) {
+                hash = hash!.Lower();
+                switch (hash.Length) {
+                    case 32:
+                        string md5 = CryptographyUtils.ComputeFileHash(localPath, CryptographyUtils.HashMethod.Md5);
+                        if (hash != md5) return "文件 MD5 应为 " + hash + ",实际为 " + md5;
+                        break;
+                    case 40:
+                        string sha1 = CryptographyUtils.ComputeFileHash(localPath, CryptographyUtils.HashMethod.Sha1);
+                        if (hash != sha1) return "文件 SHA1 应为 " + hash + ",实际为 " + sha1;
+                        break;
+                    case 64:
+                        string sha256 = CryptographyUtils.ComputeFileHash(localPath, CryptographyUtils.HashMethod.Sha256);
+                        if (hash != sha256) return "文件 SHA256 应为 " + hash + ",实际为 " + sha256;
+                        break;
+                    case 128:
+                        string sha512 = CryptographyUtils.ComputeFileHash(localPath, CryptographyUtils.HashMethod.Sha512);
+                        if (hash != sha512) return "文件 SHA512 应为 " + hash + ",实际为 " + sha512;
+                        break;
+                    default:
+                        return $"无法识别的 Hash 类型(长度为 {hash.Length}):{hash}";
+                }
+            }
+
+            if (IsJson) {
+                string content = FileUtils.ReadAsString(localPath);
+                if (content == "") return "读取到的文件为空";
+                //GetJson(content); // 失败会抛出异常
+            }
+
+            return null;
+        } catch (Exception ex) {
+            Logger.Warn(ex, "检查文件出错");
+            return ex.GetDisplay(false);
+        }
+    }
+}