Przeglądaj źródła

ref: DirectoryUtils.Copy

龙腾猫跃 5 miesięcy temu
rodzic
commit
e391dd22b6

+ 3 - 3
Shared/Extensions/StringExtensions.cs

@@ -185,7 +185,7 @@ public static class StringExtensions {
         try {
             return Regex.Matches(str, pattern, options).Cast<Match>().Select(m => m.Value);
         } catch (Exception ex) {
-            Logger.Warning(ex, $"{nameof(RegexSearch)} 失败({pattern})");
+            Logger.Warn(ex, $"{nameof(RegexSearch)} 失败({pattern})");
             return [];
         }
     }
@@ -198,7 +198,7 @@ public static class StringExtensions {
             var match = Regex.Match(str, pattern, options);
             return match.Success ? match.Value : null;
         } catch (Exception ex) {
-            Logger.Warning(ex, $"{nameof(RegexSeek)} 失败({pattern})");
+            Logger.Warn(ex, $"{nameof(RegexSeek)} 失败({pattern})");
             return null;
         }
     }
@@ -210,7 +210,7 @@ public static class StringExtensions {
         try {
             return Regex.IsMatch(str, pattern, options);
         } catch (Exception ex) {
-            Logger.Warning(ex, $"{nameof(RegexCheck)} 失败({pattern})");
+            Logger.Warn(ex, $"{nameof(RegexCheck)} 失败({pattern})");
             return false;
         }
     }

+ 11 - 28
Shared/General/Logger.cs

@@ -6,24 +6,7 @@ namespace MeloongCore;
 /// <summary>
 /// 日志等级。
 /// </summary>
-public enum LogLevel {
-    /// <summary>
-    /// 追踪。
-    /// </summary>
-    Trace,
-    /// <summary>
-    /// 信息。
-    /// </summary>
-    Info,
-    /// <summary>
-    /// 警告。
-    /// </summary>
-    Warning,
-    /// <summary>
-    /// 错误。
-    /// </summary>
-    Error
-}
+public enum LogLevel { Trace, Info, Warn, Error }
 
 /// <summary>
 /// 输出日志时执行的错误汇报行为。
@@ -65,8 +48,8 @@ public static class Logger {
         => Log(message, LogLevel.Trace, behavior, filePath);
     public static void Info(string message, LogBehavior behavior = LogBehavior.None, [CallerFilePath] string filePath = "") 
         => Log(message, LogLevel.Info, behavior, filePath);
-    public static void Warning(string message, LogBehavior behavior = LogBehavior.ToastIfDebug, [CallerFilePath] string filePath = "") 
-        => Log(message, LogLevel.Warning, behavior, filePath);
+    public static void Warn(string message, LogBehavior behavior = LogBehavior.ToastIfDebug, [CallerFilePath] string filePath = "") 
+        => Log(message, LogLevel.Warn, behavior, filePath);
     public static void Error(string message, LogBehavior behavior = LogBehavior.AlertThenFeedback, [CallerFilePath] string filePath = "") 
         => Log(message, LogLevel.Error, behavior, filePath);
 
@@ -76,19 +59,19 @@ public static class Logger {
         => Log(messageGetter, LogLevel.Trace, behavior, filePath);
     public static void Info(Func<string> messageGetter, LogBehavior behavior = LogBehavior.None, [CallerFilePath] string filePath = "") 
         => Log(messageGetter, LogLevel.Info, behavior, filePath);
-    public static void Warning(Func<string> messageGetter, LogBehavior behavior = LogBehavior.ToastIfDebug, [CallerFilePath] string filePath = "") 
-        => Log(messageGetter, LogLevel.Warning, behavior, filePath);
+    public static void Warn(Func<string> messageGetter, LogBehavior behavior = LogBehavior.ToastIfDebug, [CallerFilePath] string filePath = "") 
+        => Log(messageGetter, LogLevel.Warn, behavior, filePath);
     public static void Error(Func<string> messageGetter, LogBehavior behavior = LogBehavior.AlertThenFeedback, [CallerFilePath] string filePath = "") 
         => Log(messageGetter, LogLevel.Error, behavior, filePath);
 
-    public static void Log(Exception ex, string? message = null, LogLevel level = LogLevel.Warning, LogBehavior behavior = LogBehavior.ToastIfDebug, [CallerFilePath] string filePath = "") 
+    public static void Log(Exception ex, string? message = null, LogLevel level = LogLevel.Warn, LogBehavior behavior = LogBehavior.ToastIfDebug, [CallerFilePath] string filePath = "") 
         => Instance.Log(ex, message, level, behavior, filePath);
     public static void Trace(Exception ex, string? message = null, LogBehavior behavior = LogBehavior.None, [CallerFilePath] string filePath = "") 
         => Log(ex, message, LogLevel.Trace, behavior, filePath);
     public static void Info(Exception ex, string? message = null, LogBehavior behavior = LogBehavior.None, [CallerFilePath] string filePath = "") 
         => Log(ex, message, LogLevel.Info, behavior, filePath);
-    public static void Warning(Exception ex, string? message = null, LogBehavior behavior = LogBehavior.ToastIfDebug, [CallerFilePath] string filePath = "") 
-        => Log(ex, message, LogLevel.Warning, behavior, filePath);
+    public static void Warn(Exception ex, string? message = null, LogBehavior behavior = LogBehavior.ToastIfDebug, [CallerFilePath] string filePath = "") 
+        => Log(ex, message, LogLevel.Warn, behavior, filePath);
     public static void Error(Exception ex, string? message = null, LogBehavior behavior = LogBehavior.AlertThenFeedback, [CallerFilePath] string filePath = "") 
         => Log(ex, message, LogLevel.Error, behavior, filePath);
 
@@ -224,9 +207,9 @@ public class FileLogger : BaseLogger, IDisposable {
                     FileUtils.Delete(olderFile);
                 }
             } catch (IOException ex) {
-                Logger.Warning(ex, "可能同时开启了多个程序,这或许会导致未知问题", LogBehavior.Toast);
+                Logger.Warn(ex, "可能同时开启了多个程序,这或许会导致未知问题", LogBehavior.Toast);
             } catch (Exception ex) {
-                Logger.Warning(ex, "整理日志文件失败");
+                Logger.Warn(ex, "整理日志文件失败");
             }
             // 写入新日志文件
             try {
@@ -238,7 +221,7 @@ public class FileLogger : BaseLogger, IDisposable {
                     Flush();
                 }
             } catch (Exception ex) {
-                Logger.Warning(ex, "写入日志文件失败", LogBehavior.Toast);
+                Logger.Warn(ex, "写入日志文件失败", LogBehavior.Toast);
                 writerAvailable = false;
             }
         }) { Name = nameof(FileLogger), Priority = ThreadPriority.Lowest, IsBackground = true };

+ 28 - 6
Shared/Utils/DirectoryUtils.cs

@@ -7,7 +7,7 @@ public static class DirectoryUtils {
     /// </summary>
     public static void Create(string path, bool isFilePath = false) {
         if (isFilePath) path = PathUtils.RemoveFileName(path);
-        if (Exists(path)) return;
+        if (DirectoryUtils.Exists(path)) return;
         Logger.Trace($"新建文件夹:{path}");
         path = PathUtils.WithLongPath(path);
         Directory.CreateDirectory(path);
@@ -29,7 +29,7 @@ public static class DirectoryUtils {
     /// 返回指定路径下的所有文件,不以 \\?\ 开头。
     /// </summary>
     public static IEnumerable<string> GetFiles(string path, bool topDirectoryOnly = false, string searchPattern = "*") {
-        if (!Exists(path)) return [];
+        if (!DirectoryUtils.Exists(path)) return [];
         Logger.Trace($"枚举文件({searchPattern},递归:{!topDirectoryOnly}):{path}");
         return Directory.EnumerateFiles(PathUtils.WithLongPath(path), searchPattern, topDirectoryOnly ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories).
             Select(PathUtils.WithoutLongPath);
@@ -39,7 +39,7 @@ public static class DirectoryUtils {
     /// 返回指定路径下的所有文件夹,不以分隔符结尾,不以 \\?\ 开头。
     /// </summary>
     public static IEnumerable<string> GetDirectories(string path, bool topDirectoryOnly = false, string searchPattern = "*") {
-        if (!Exists(path)) return [];
+        if (!DirectoryUtils.Exists(path)) return [];
         Logger.Trace($"枚举文件夹({searchPattern},递归:{!topDirectoryOnly}):{path}");
         return Directory.EnumerateDirectories(PathUtils.WithLongPath(path), searchPattern, topDirectoryOnly ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories).
             Select(PathUtils.WithoutLongPath);
@@ -50,15 +50,37 @@ public static class DirectoryUtils {
     /// 如果文件夹不存在,也返回 true。
     /// </summary>
     public static bool IsEmpty(string path) {
-        if (!Exists(path)) return true;
+        if (!DirectoryUtils.Exists(path)) return true;
         return !Directory.EnumerateFileSystemEntries(PathUtils.WithLongPath(path)).Any();
     }
 
+    /// <summary>
+    /// 复制文件夹。
+    /// 若原文件夹不存在,则不执行。
+    /// </summary>
+    public static void Copy(string sourceDirName, string destDirName, Action<double>? progressHandler = null) {
+        if (!DirectoryUtils.Exists(sourceDirName)) {
+            Logger.Info($"尝试复制文件夹,但原文件夹不存在,已跳过复制:{sourceDirName} → {destDirName}");
+            progressHandler?.Invoke(1);
+            return;
+        }
+        sourceDirName = PathUtils.WithoutLongPath(PathUtils.WithSeparator(sourceDirName, Path.DirectorySeparatorChar));
+        destDirName = PathUtils.WithoutLongPath(PathUtils.WithSeparator(destDirName, Path.DirectorySeparatorChar));
+        int doneCount = 0;
+        Lazy<int> totalCount = new(() => DirectoryUtils.GetFiles(sourceDirName).Count());
+        Logger.Trace($"复制文件夹:{sourceDirName} → {destDirName}");
+        foreach (var file in DirectoryUtils.GetFiles(sourceDirName)) {
+            FileUtils.Copy(file, file.Replace(sourceDirName, destDirName));
+            doneCount++;
+            if (progressHandler is not null) progressHandler?.Invoke((double) doneCount / totalCount.Value);
+        }
+    }
+
     /// <summary>
     /// 剪切文件夹。
     /// </summary>
     public static void Move(string sourceDirName, string destDirName) {
-        if (!Exists(sourceDirName)) return;
+        if (!DirectoryUtils.Exists(sourceDirName)) return;
         DirectoryUtils.Delete(destDirName); // Move 要求不存在对应文件夹
         Logger.Trace($"剪切文件夹:{sourceDirName} → {destDirName}");
         Directory.Move(PathUtils.WithLongPath(sourceDirName), PathUtils.WithLongPath(destDirName));
@@ -68,7 +90,7 @@ public static class DirectoryUtils {
     /// 删除文件夹。
     /// </summary>
     public static void Delete(string dirPath, bool toRecycleBin = false) {
-        if (!Exists(dirPath)) return;
+        if (!DirectoryUtils.Exists(dirPath)) return;
         Logger.Trace($"{(toRecycleBin ? "将文件夹删除到回收站" : "删除文件夹")}:{dirPath}");
         if (toRecycleBin) {
             FileUtils.DeleteToRecycleBin(dirPath);

+ 7 - 1
Shared/Utils/FileUtils.cs

@@ -1,4 +1,5 @@
-using System.IO.Compression;
+using System.IO;
+using System.IO.Compression;
 
 namespace MeloongCore;
 public static class FileUtils {
@@ -271,9 +272,14 @@ public static class FileUtils {
     /// <summary>
     /// 复制文件。
     /// 会创建对应文件夹、覆盖已有的文件。
+    /// 若原文件不存在,则不执行。
     /// </summary>
     public static void Copy(string sourceFilePath, string destFilePath) {
         if (sourceFilePath == destFilePath) return; // 如果复制同一个文件则跳过
+        if (!FileUtils.Exists(sourceFilePath)) {
+            Logger.Info($"尝试复制文件,但原文件不存在,已跳过复制:{sourceFilePath} → {destFilePath}");
+            return;
+        }
         DirectoryUtils.Create(destFilePath, isFilePath: true);
         Logger.Trace($"复制文件:{sourceFilePath} → {destFilePath}");
         File.Copy(PathUtils.WithLongPath(sourceFilePath), PathUtils.WithLongPath(destFilePath), true);

+ 10 - 2
Shared/Utils/PathUtils.cs

@@ -4,10 +4,18 @@ public static class PathUtils {
     #region 分隔符
 
     /// <summary>
-    /// 确保路径的结尾包含文件夹分隔符。
+    /// 确保路径的末尾包含任意文件夹分隔符。
     /// </summary>
     public static string WithSeparator(string folder) 
-        => folder.EndsWithF(Path.DirectorySeparatorChar) || folder.EndsWithF(Path.AltDirectorySeparatorChar) ? folder : folder + Path.DirectorySeparatorChar;
+        => folder + (folder.EndsWithF(Path.DirectorySeparatorChar) || folder.EndsWithF(Path.AltDirectorySeparatorChar) ? "" : Path.DirectorySeparatorChar);
+
+    /// <summary>
+    /// 确保路径使用指定的文件夹分隔符,且末尾包含指定的文件夹分隔符。
+    /// </summary>
+    public static string WithSeparator(string folder, char separator) {
+        folder = folder.Replace(Path.DirectorySeparatorChar, separator).Replace(Path.AltDirectorySeparatorChar, separator);
+        return folder + (folder.EndsWithF(separator) ? "" : separator);
+    }
 
     /// <summary>
     /// 确保路径的结尾不包含文件夹分隔符。