From 9ee82200dab479911a49b260a7531aca81e4d10f Mon Sep 17 00:00:00 2001 From: Juster Zhu Date: Tue, 16 Jan 2024 22:59:58 +0800 Subject: [PATCH] feature:Encapsulate file processing classes --- .../GeneralUpdate.Client.csproj | 1 + src/c#/GeneralUpdate.Client/MySample.cs | 9 +- src/c#/GeneralUpdate.Client/Program.cs | 30 +- .../ContentProvider/FileNode.cs | 148 ++++++++++ .../ContentProvider/FileProvider.cs | 261 ++++++++++++++++++ .../ContentProvider/FileTree.cs | 173 ++++++++++++ src/c#/GeneralUpdate.Core/Utils/FileUtil.cs | 7 +- .../WillMessage/WillMessageManager.cs | 24 +- .../DifferentialCore.cs | 2 +- 9 files changed, 622 insertions(+), 33 deletions(-) create mode 100644 src/c#/GeneralUpdate.Core/ContentProvider/FileNode.cs create mode 100644 src/c#/GeneralUpdate.Core/ContentProvider/FileProvider.cs create mode 100644 src/c#/GeneralUpdate.Core/ContentProvider/FileTree.cs diff --git a/src/c#/GeneralUpdate.Client/GeneralUpdate.Client.csproj b/src/c#/GeneralUpdate.Client/GeneralUpdate.Client.csproj index 02b2970f..94ea81d5 100644 --- a/src/c#/GeneralUpdate.Client/GeneralUpdate.Client.csproj +++ b/src/c#/GeneralUpdate.Client/GeneralUpdate.Client.csproj @@ -5,6 +5,7 @@ net8.0 enable enable + diff --git a/src/c#/GeneralUpdate.Client/MySample.cs b/src/c#/GeneralUpdate.Client/MySample.cs index ba98593c..3a35b76c 100644 --- a/src/c#/GeneralUpdate.Client/MySample.cs +++ b/src/c#/GeneralUpdate.Client/MySample.cs @@ -301,24 +301,25 @@ private bool IsDriverFile(string filePath) => #endregion + #region 娴嬭瘯WillMessage public void TestWillMessage() { var path1 = "D:\\packet\\source"; var path2 = "D:\\packet\\target"; - var hash = ""; + var hash = "28d10f1fc2a23dd1afe0af40d132b25c72ea56005963f653c27889f03d381c8d"; for (int i = 0; i < 1; i++) { - var version = "1.0.0" + i; + var version = "1.0.0." + i; WillMessageManager.Instance.Backup(path1,path2, version, hash, 1); } WillMessageManager.Instance.Builder(); - WillMessageManager.Instance.GetWillMessage(); + var obj = WillMessageManager.Instance.GetWillMessage(); WillMessageManager.Instance.Check(); WillMessageManager.Instance.Restore(); - WillMessageManager.Instance.Clear(); + //WillMessageManager.Instance.Clear(); } #endregion diff --git a/src/c#/GeneralUpdate.Client/Program.cs b/src/c#/GeneralUpdate.Client/Program.cs index 99480e10..6bc7d2bd 100644 --- a/src/c#/GeneralUpdate.Client/Program.cs +++ b/src/c#/GeneralUpdate.Client/Program.cs @@ -6,22 +6,24 @@ internal class Program { static void Main(string[] args) { - Task.Run(async() => - { - //415eed05eb310f480d1e4d15516fa00e484ddb9f416908b217f17b782ded2030 - //var zip1 = @"D:\github_project\WpfClient\WebApi\UpdateFiles\WpfClient_1_24.1.5.1218.zip"; - //94bd3d806d39cd1b8813298ec0637c7f377658e766845a06cc50917306cb4ad9 - //var zip2 = @"D:\github_project\WpfClient\WebApi\UpdateFiles\WpfClient_1_24.1.5.1224.zip"; + MySample sample = new MySample(); + sample.TestWillMessage(); + //Task.Run(async() => + //{ + // //415eed05eb310f480d1e4d15516fa00e484ddb9f416908b217f17b782ded2030 + // //var zip1 = @"D:\github_project\WpfClient\WebApi\UpdateFiles\WpfClient_1_24.1.5.1218.zip"; + // //94bd3d806d39cd1b8813298ec0637c7f377658e766845a06cc50917306cb4ad9 + // //var zip2 = @"D:\github_project\WpfClient\WebApi\UpdateFiles\WpfClient_1_24.1.5.1224.zip"; - //var hashAlgorithm = new Sha256HashAlgorithm(); - //var hashSha256 = hashAlgorithm.ComputeHash(zip1); - //var hashSha2561 = hashAlgorithm.ComputeHash(zip2); + // //var hashAlgorithm = new Sha256HashAlgorithm(); + // //var hashSha256 = hashAlgorithm.ComputeHash(zip1); + // //var hashSha2561 = hashAlgorithm.ComputeHash(zip2); - MySample sample = new MySample(); - //await sample.TestDifferentialClean(); - //await sample.TestDifferentialDirty(); - await sample.Upgrade(); - }); + // MySample sample = new MySample(); + // //await sample.TestDifferentialClean(); + // //await sample.TestDifferentialDirty(); + // await sample.Upgrade(); + //}); Console.Read(); } } diff --git a/src/c#/GeneralUpdate.Core/ContentProvider/FileNode.cs b/src/c#/GeneralUpdate.Core/ContentProvider/FileNode.cs new file mode 100644 index 00000000..bb0851f2 --- /dev/null +++ b/src/c#/GeneralUpdate.Core/ContentProvider/FileNode.cs @@ -0,0 +1,148 @@ +using System; + +namespace GeneralUpdate.Differential.ContentProvider +{ + public class FileNode + { + #region Public Properties + + public long Id { get; set; } + + public string Name { get; set; } + + public string FullName { get; set; } + + public string Path { get; set; } + + public string Hash { get; set; } + + public FileNode Left { get; set; } + + public FileNode Right { get; set; } + + public int LeftType { get; set; } + + public int RightType { get; set; } + + public string RelativePath { get; set; } + + #endregion Public Properties + + #region Constructors + + public FileNode() + { } + + public FileNode(int id) + { + Id = id; + } + + #endregion Constructors + + #region Public Methods + + public void Add(FileNode node) + { + if (node == null) return; + + if (node.Id < Id) + { + if (Left == null) + { + Left = node; + } + else + { + Left.Add(node); + } + } + else + { + if (Right == null) + { + Right = node; + } + else + { + Right.Add(node); + } + } + } + + public void InfixOrder() + { + if (Left != null) + { + Left.InfixOrder(); + } + if (Right != null) + { + Right.InfixOrder(); + } + } + + public FileNode Search(long id) + { + if (id == Id) + { + return this; + } + else if (id < Id) + { + if (Left == null) return null; + return Left.Search(id); + } + else + { + if (Right == null) return null; + return Right.Search(id); + } + } + + /// + /// Find the parent node of the node that you want to delete. + /// + /// + /// + public FileNode SearchParent(long id) + { + if (Left != null && Left.Id == id || Right != null && Right.Id == id) + { + return this; + } + else + { + if (id < Id && Left != null) + { + return Left.SearchParent(id); + } + else if (id >= Id && Right != null) + { + return Right.SearchParent(id); + } + else + { + return null; + } + } + } + + /// + /// Compare tree nodes equally by Hash and file names. + /// + /// + /// + public override bool Equals(object obj) + { + if (obj == null) return false; + var tempNode = obj as FileNode; + if (tempNode == null) throw new ArgumentException(nameof(tempNode)); + return string.Equals(Hash, tempNode.Hash,StringComparison.OrdinalIgnoreCase) && string.Equals(Name, tempNode.Name,StringComparison.OrdinalIgnoreCase); + } + + public override int GetHashCode() => base.GetHashCode(); + + #endregion Public Methods + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/ContentProvider/FileProvider.cs b/src/c#/GeneralUpdate.Core/ContentProvider/FileProvider.cs new file mode 100644 index 00000000..7a039544 --- /dev/null +++ b/src/c#/GeneralUpdate.Core/ContentProvider/FileProvider.cs @@ -0,0 +1,261 @@ +using GeneralUpdate.Core.HashAlgorithms; +using GeneralUpdate.Differential.Common; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace GeneralUpdate.Differential.ContentProvider +{ + public enum FileOperations + { + Query, + Delete, + Update, + Add, + Copy + } + + public enum SetOperations + { + Intersection, + Union, + Difference + } + + public class FileProvider + { + private long _fileCount = 0; + + #region Public Methods + + /// + /// Compare two binary trees with different children. + /// + /// Left tree folder path. + /// Right tree folder path. + /// ValueTuple(leftFileNodes,rightFileNodes, differentTreeNode) + public async Task, IEnumerable, IEnumerable>> Compare(string leftPath, string rightPath) + { + return await Task.Run(() => + { + ResetId(); + var leftFileNodes = Read(leftPath); + var rightFileNodes = Read(rightPath); + var leftTree = new FileTree(leftFileNodes); + var rightTree = new FileTree(rightFileNodes); + var differentTreeNode = new List(); + leftTree.Compare(leftTree.GetRoot(), rightTree.GetRoot(), ref differentTreeNode); + return ValueTuple.Create(leftFileNodes, rightFileNodes, differentTreeNode); + }); + } + + /// + /// Using the list on the left as a baseline, find the set of differences between the two file lists. + /// + /// Previous version file list path + /// The current version file list path + /// Except collection + public async Task> Except(string leftPath, string rightPath) + { + return await Task.Run(() => + { + var leftFileNodes = Read(leftPath); + var rightFileNodes = Read(rightPath); + var rightNodeDic = rightFileNodes.ToDictionary(x => x.RelativePath, x => x); + var filesOnlyInLeft = leftFileNodes.Where(f => !rightNodeDic.ContainsKey(f.RelativePath)).ToList(); + return filesOnlyInLeft; + }); + } + + #endregion Public Methods + + #region Private Methods + + /// + /// Recursively read all files in the folder path. + /// + /// folder path. + /// folder root path. + /// different chalders. + private IEnumerable Read(string path, string rootPath = null) + { + var resultFiles = new List(); + if (string.IsNullOrEmpty(rootPath)) rootPath = path; + if (!rootPath.EndsWith("/")) rootPath += "/"; + Uri rootUri = new Uri(rootPath); + foreach (var subPath in Directory.GetFiles(path)) + { + if (IsMatchBlacklist(subPath)) continue; + + var hashAlgorithm = new Sha256HashAlgorithm(); + var hash = hashAlgorithm.ComputeHash(subPath); + var subFileInfo = new FileInfo(subPath); + Uri subUri = new Uri(subFileInfo.FullName); + resultFiles.Add(new FileNode() { Id = GetId(), Path = path, Name = subFileInfo.Name, Hash = hash, FullName = subFileInfo.FullName, RelativePath = rootUri.MakeRelativeUri(subUri).ToString() }); + } + foreach (var subPath in Directory.GetDirectories(path)) + { + resultFiles.AddRange(Read(subPath, rootPath)); + } + return resultFiles; + } + + /// + /// Self-growing file tree node ID. + /// + /// + private long GetId() => Interlocked.Increment(ref _fileCount); + + private void ResetId() => Interlocked.Exchange(ref _fileCount, 0); + + /// + /// Whether the file name in the file path can match the blacklisted file. + /// + /// + /// + private bool IsMatchBlacklist(string subPath) + { + var blackList = Filefilter.GetBlackFiles(); + if (blackList == null) return false; + foreach (var file in blackList) + { + if (subPath.Contains(file)) return true; + } + return false; + } + + #endregion Private Methods + + /// + /// 根据参数内容筛选出sourceDir、targetDir两个文件路径中符合要求的文件信息 + /// + /// 源目录 + /// 目标目录 + /// 筛选结果保存目录 + /// 筛选条件,可以是文件名,可以是文件后缀名 + /// 文件可执行的操作:增、删、查、改、拷贝 + /// 集合运算:交集、并集、差集 + /// 整个方法里的操作是否递归执行 + /// 是否保持目录结构的完整性 + /// + //public List Handle(string sourceDir,string targetDir, string resultDir, List condition, FileOperations fileOption, SetOperations setOperations,bool recursion,bool integrality) + //{ + // return new List(); + //} + + public List Handle(string sourceDir, string targetDir, string resultDir, List condition, FileOperations fileOption, SetOperations setOperations, bool recursion, bool integrality) + { + // 首先获取源目录和目标目录中所有的文件信息 + IEnumerable sourceFiles = Directory.EnumerateFiles(sourceDir, "*.*", recursion ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly) + .Select(path => new FileInfo(path)) + .Select(fileInfo => new FileNode + { + Id = fileInfo.GetHashCode(), + Name = fileInfo.Name, + FullName = fileInfo.FullName, + Path = fileInfo.DirectoryName, + Hash = CalculateFileHash(fileInfo) + }); + IEnumerable targetFiles = Directory.EnumerateFiles(targetDir, "*.*", recursion ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly) + .Select(path => new FileInfo(path)) + .Select(fileInfo => new FileNode + { + Id = fileInfo.GetHashCode(), + Name = fileInfo.Name, + FullName = fileInfo.FullName, + Path = fileInfo.DirectoryName, + Hash = CalculateFileHash(fileInfo) + }); + + // 然后根据条件筛选满足要求的文件 + IEnumerable filteredSourceFiles = sourceFiles.Where(file => condition.Any(c => file.Name.Contains(c) || file.Name.EndsWith(c))); + IEnumerable filteredTargetFiles = targetFiles.Where(file => condition.Any(c => file.Name.Contains(c) || file.Name.EndsWith(c))); + + // 进行集合运算以得到最终的结果 + IEnumerable resultFiles; + switch (setOperations) + { + case SetOperations.Intersection: + resultFiles = filteredSourceFiles.Intersect(filteredTargetFiles); + break; + case SetOperations.Union: + resultFiles = filteredSourceFiles.Union(filteredTargetFiles); + break; + case SetOperations.Difference: + resultFiles = filteredSourceFiles.Except(filteredTargetFiles); + break; + default: + throw new ArgumentException("Invalid operation.", nameof(setOperations)); + } + + // 执行文件操作 + foreach (FileNode file in resultFiles) + { + ExecuteFileOperation(file, fileOption, resultDir, integrality); + } + + return resultFiles.ToList(); + } + + /// + /// 计算文件的哈希值. + /// + /// 文件对象. + /// + private string CalculateFileHash(FileInfo file) + { + // 这里只是一个占位符,实际上你需要实现一个方法来计算文件哈希值. + return string.Empty; + } + + /// + /// 执行文件操作. + /// + /// 文件对象. + /// 要执行的操作. + /// 结果目录. + /// 是否保留结构. + private void ExecuteFileOperation(FileNode file, FileOperations operation, string resultDir, bool retainStructure) + { + string destinationPath = Path.Combine(resultDir, retainStructure ? file.RelativePath : file.Name); + + switch (operation) + { + case FileOperations.Add: + // 如果文件已存在,此操作将覆盖现有文件 + File.Copy(file.Path, destinationPath, true); + break; + case FileOperations.Delete: + if (File.Exists(destinationPath)) + { + File.Delete(destinationPath); + } + break; + case FileOperations.Update: + // 在这里,我们假定更新操作意味着复制源文件到目标位置,覆盖现有文件 + File.Copy(file.Path, destinationPath, true); + break; + case FileOperations.Copy: + // 确保目标目录存在 + string directoryName = Path.GetDirectoryName(destinationPath); + if (!Directory.Exists(directoryName)) + { + Directory.CreateDirectory(directoryName); + } + // 拷贝文件到新的位置 + File.Copy(file.Path, destinationPath, true); + break; + case FileOperations.Query: + // 对于“查询”操作,我们不执行任何文件操作,只在控制台中打印出相关信息 + Console.WriteLine($"Found file: {file.FullName}"); + break; + default: + throw new ArgumentException("Invalid operation", nameof(operation)); + } + } + + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/ContentProvider/FileTree.cs b/src/c#/GeneralUpdate.Core/ContentProvider/FileTree.cs new file mode 100644 index 00000000..b77d54f2 --- /dev/null +++ b/src/c#/GeneralUpdate.Core/ContentProvider/FileTree.cs @@ -0,0 +1,173 @@ +锘縰sing System.Collections.Generic; +using System.Diagnostics; + +namespace GeneralUpdate.Differential.ContentProvider +{ + /// + /// Simple file binary tree. + /// + public class FileTree + { + #region Private Members + + private FileNode _root; + + #endregion Private Members + + #region Constructors + + public FileTree() + { } + + public FileTree(IEnumerable nodes) + { + foreach (var node in nodes) Add(node); + } + + #endregion Constructors + + #region Public Methods + + public void Add(FileNode node) + { + if (_root == null) + { + _root = node; + } + else + { + _root.Add(node); + } + } + + public void InfixOrder() + { + if (_root != null) + { + _root.InfixOrder(); + } + else + { + Debug.WriteLine("The binary sort tree is empty and cannot be traversed锛"); + } + } + + public FileNode Search(long id) => _root == null ? null : _root.Search(id); + + public FileNode SearchParent(long id) => _root == null ? null : _root.SearchParent(id); + + public long DelRightTreeMin(FileNode node) + { + FileNode target = node; + while (target.Left != null) + { + target = target.Left; + } + DelNode(target.Id); + return target.Id; + } + + public void DelNode(long id) + { + if (_root == null) + { + return; + } + else + { + FileNode targetNode = Search(id); + if (targetNode == null) + { + return; + } + if (_root.Left == null && _root.Right == null) + { + _root = null; + return; + } + + FileNode parent = SearchParent(id); + if (targetNode.Left == null && targetNode.Right == null) + { + if (parent.Left != null && parent.Left.Id == id) + { + parent.Left = null; + } + else if (parent.Right != null && parent.Right.Id == id) + { + parent.Right = null; + } + } + else if (targetNode.Left != null && targetNode.Right != null) + { + long minVal = DelRightTreeMin(targetNode.Right); + targetNode.Id = minVal; + } + else + { + if (targetNode.Left != null) + { + if (parent.Left.Id == id) + { + parent.Left = targetNode.Left; + } + else + { + parent.Right = targetNode.Left; + } + } + else + { + if (parent.Left.Id == id) + { + parent.Left = targetNode.Right; + } + else + { + parent.Right = targetNode.Right; + } + } + } + } + } + + /// + /// Starting from the root node, recursively compares two different child nodes of the binary tree and nodes that are not included. + /// + /// + /// + /// + public void Compare(FileNode node, FileNode node0, ref List nodes) + { + if (node != null && node.Left != null) + { + if (!node.Equals(node0) && node0 != null) nodes.Add(node0); + Compare(node.Left, node0.Left, ref nodes); + } + else if (node0 != null && node0.Left != null) + { + nodes.Add(node0); + Compare(node.Left, node0.Left, ref nodes); + } + + if (node != null && node.Right != null) + { + if (!node.Equals(node0) && node0 != null) nodes.Add(node0); + Compare(node.Right, node0 == null ? null : node0.Right, ref nodes); + } + else if (node0 != null && node0.Right != null) + { + nodes.Add(node0); + Compare(node == null ? null : node.Right, node0.Right, ref nodes); + } + else if (node0 != null) + { + nodes.Add(node0); + } + } + + public FileNode GetRoot() => _root; + + #endregion Public Methods + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Utils/FileUtil.cs b/src/c#/GeneralUpdate.Core/Utils/FileUtil.cs index 482dec9c..c11d6766 100644 --- a/src/c#/GeneralUpdate.Core/Utils/FileUtil.cs +++ b/src/c#/GeneralUpdate.Core/Utils/FileUtil.cs @@ -37,13 +37,12 @@ public static FileInfo[] GetAllFiles(string path) } } - public static void CreateJson(string targetPath,string fileName,T obj) + public static void CreateJson(string targetPath,T obj) { if (!Directory.Exists(targetPath)) Directory.CreateDirectory(targetPath); - var fileFullPath = Path.Combine(targetPath,fileName); - if (File.Exists(fileFullPath)) File.Delete(fileFullPath); + if (File.Exists(targetPath)) File.Delete(targetPath); var jsonString = JsonConvert.SerializeObject(obj); - File.WriteAllText(fileFullPath, jsonString); + File.WriteAllText(targetPath, jsonString); } public static T GetJson(string path) diff --git a/src/c#/GeneralUpdate.Core/WillMessage/WillMessageManager.cs b/src/c#/GeneralUpdate.Core/WillMessage/WillMessageManager.cs index b5f014ef..43bc50bc 100644 --- a/src/c#/GeneralUpdate.Core/WillMessage/WillMessageManager.cs +++ b/src/c#/GeneralUpdate.Core/WillMessage/WillMessageManager.cs @@ -10,11 +10,11 @@ namespace GeneralUpdate.Core.WillMessage public class WillMessageManager { #region Private Members - - public const string DEFULT_WILL_MESSAGE_DIR = @"C:\generalupdate_willmessages"; + private string TempPath = Path.GetTempPath(); + public const string DEFULT_WILL_MESSAGE_DIR = "generalupdate_willmessages"; + public const string BACKUP_DIR = "generalupdate_backup"; public const string DEFULT_WILL_MESSAGE_FILE = "will_message.json"; - - public const string BACKUP_ROOT_PATH = @"C:\generalupdate_backup"; + private string _packetPath; private string _appPath; private string _backupPath; @@ -74,15 +74,15 @@ public void Clear() _willMessageFile = null; _backupStack.Clear(); FileUtil.DeleteDir(DEFULT_WILL_MESSAGE_DIR); - FileUtil.DeleteDir(BACKUP_ROOT_PATH); + FileUtil.DeleteDir(GetBackupPath()); } public void Backup(string appPath, string packetPath, string version,string hash,int appType) { - if (!Directory.Exists(BACKUP_ROOT_PATH)) - Directory.CreateDirectory(BACKUP_ROOT_PATH); + if (!Directory.Exists(GetBackupPath())) + Directory.CreateDirectory(GetBackupPath()); - var versionDir = Path.Combine(BACKUP_ROOT_PATH, version, appType == 1 ? "ClientApp" : "UpgradeApp"); + var versionDir = Path.Combine(GetBackupPath(), version, appType == 1 ? "ClientApp" : "UpgradeApp"); if (!Directory.Exists(versionDir)) Directory.CreateDirectory(versionDir); @@ -115,7 +115,7 @@ public void Builder() .SetCreateTime(DateTime.Now) .SetChangeTime(DateTime.Now) .Build(); - FileUtil.CreateJson(Path.Combine(DEFULT_WILL_MESSAGE_DIR, DateTime.Now.ToString("yyyyMMdd")), DEFULT_WILL_MESSAGE_FILE, _willMessage); + FileUtil.CreateJson(GetWillMessagePath(), _willMessage); } public void Check() @@ -146,7 +146,11 @@ public void Check() #region Private Methods - private string GetWillMessagePath() => Path.Combine(DEFULT_WILL_MESSAGE_DIR, DateTime.Now.ToString("yyyyMMdd"), DEFULT_WILL_MESSAGE_FILE); + private string GetWillMessagePath() => + Path.Combine(TempPath, DEFULT_WILL_MESSAGE_DIR, DateTime.Now.ToString("yyyyMMdd"), DEFULT_WILL_MESSAGE_FILE); + + private string GetBackupPath() => + Path.Combine(TempPath, BACKUP_DIR); private void ProcessDirectory(string targetDirectory, string basePath, string destPath) { diff --git a/src/c#/GeneralUpdate.Differential/DifferentialCore.cs b/src/c#/GeneralUpdate.Differential/DifferentialCore.cs index 44ec20b8..a63a3369 100644 --- a/src/c#/GeneralUpdate.Differential/DifferentialCore.cs +++ b/src/c#/GeneralUpdate.Differential/DifferentialCore.cs @@ -117,7 +117,7 @@ public async Task Clean(string sourcePath, string targetPath, string patchPath = //If a file is found that needs to be deleted, a list of files is written to the update package. var exceptFiles = await fileProvider.Except(sourcePath, targetPath); if(exceptFiles != null && exceptFiles.Count() > 0) - FileUtil.CreateJson(patchPath, DELETE_FILES_NAME, exceptFiles); + FileUtil.CreateJson(Path.Combine(patchPath, DELETE_FILES_NAME), exceptFiles); } catch (Exception ex) {