From 687b68db3175c558da7228cfda61535560440195 Mon Sep 17 00:00:00 2001 From: Sakurai Date: Sat, 23 Mar 2024 17:50:56 +0900 Subject: [PATCH] first commit --- .gitignore | 7 ++ LICENSE | 21 +++++ README.md | 37 +++++++++ src/Application/CommandLineHelper.cs | 42 ++++++++++ src/Application/IDatabase.cs | 14 ++++ src/Application/IOHelper.cs | 82 ++++++++++++++++++++ src/Application/Zipper.cs | 112 +++++++++++++++++++++++++++ src/Application/ZipperConfig.cs | 17 ++++ src/Database/FileDatabase.cs | 61 +++++++++++++++ src/Program.cs | 81 +++++++++++++++++++ src/ZipArchiver.csproj | 27 +++++++ src/ZipArchiver.sln | 25 ++++++ 12 files changed, 526 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/Application/CommandLineHelper.cs create mode 100644 src/Application/IDatabase.cs create mode 100644 src/Application/IOHelper.cs create mode 100644 src/Application/Zipper.cs create mode 100644 src/Application/ZipperConfig.cs create mode 100644 src/Database/FileDatabase.cs create mode 100644 src/Program.cs create mode 100644 src/ZipArchiver.csproj create mode 100644 src/ZipArchiver.sln diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..81f6e9b --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +bin +obj + +.vscode +*.user + +Properties diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c9bac30 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Amer Koleci and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..58e55b1 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +ZipArchiver +=== + +設定ファイルで指定されたフォルダからZIPファイルを作成するプログラム。 + +## 動作環境 + +.NET6 SDK + +### 動作確認済みの環境 + +- Windows11 +- Debian11 + +## 設定ファイルの例 + +XML形式で指定します。 +フォルダパスについては、環境に応じて、変更してください。 +```xml + + + + D:\DataFolder + + D:\Backups + + true + + + 1 + + + D:\DataFolder\ZIPに含めない1 + D:\DataFolder\ZIPに含めない2 + + +``` diff --git a/src/Application/CommandLineHelper.cs b/src/Application/CommandLineHelper.cs new file mode 100644 index 0000000..bb49f40 --- /dev/null +++ b/src/Application/CommandLineHelper.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ZipArchiver.Application +{ + public class CommandLineHelper + { + private Dictionary _commands = new Dictionary(); + public CommandLineHelper() + { + InitCommands(); + } + + public bool HasValue(string key) + { + return _commands.ContainsKey(key); + } + public string GetValue(string key) + { + return _commands[key]; + } + + private void InitCommands() + { + string[] args = Environment.GetCommandLineArgs(); + foreach (string arg in args) + { + string[] options = arg.Split("="); + if (options.Length < 2) + { + continue; + } + + string opValue = IOHelper.GetRelatedPath(options[0], arg); + _commands.Add(options[0], opValue); + } + } + } +} diff --git a/src/Application/IDatabase.cs b/src/Application/IDatabase.cs new file mode 100644 index 0000000..d0ff0c6 --- /dev/null +++ b/src/Application/IDatabase.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ZipArchiver.Application +{ + public interface IDatabase + { + public ZipperConfig Read(); + public void Write(ZipperConfig config); + } +} diff --git a/src/Application/IOHelper.cs b/src/Application/IOHelper.cs new file mode 100644 index 0000000..bcdbddc --- /dev/null +++ b/src/Application/IOHelper.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; +using System.Reflection; + +namespace ZipArchiver.Application +{ + public class IOHelper + { + public static void CreateDirectory(string dirName) + { + if (!Path.IsPathRooted(dirName)) + { + string parent = Path.GetDirectoryName(dirName); + if (!Directory.Exists(parent)) + { + CreateDirectory(parent); + } + } + + Directory.CreateDirectory(dirName); + } + + public static string GetRelatedPath(string str1, string str2) + { + if (str1.Length >= str2.Length) + { + return str2; + } + + return str2.Substring(str1.Length); + } + + public static string GetAppFile(string fileName) + { + return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); + } + + public static long GetFileCount(string dirName) + { + long count = 0; + + DirectoryInfo dir = new DirectoryInfo(dirName); + foreach (FileInfo file in dir.EnumerateFiles()) + { + count++; + } + + foreach (DirectoryInfo dirInfo in dir.EnumerateDirectories()) + { + count += GetFileCount(dirInfo.FullName); + } + + return count; + } + + public static long GetFileCount(string dirName, IEnumerable excludes) + { + long count = 0; + + DirectoryInfo dir = new DirectoryInfo(dirName); + foreach (FileInfo file in dir.EnumerateFiles()) + { + count++; + } + + foreach (DirectoryInfo dirInfo in dir.EnumerateDirectories()) + { + if (excludes.Any(s => dirInfo.FullName == s)) + { + continue; + } + count += GetFileCount(dirInfo.FullName); + } + + return count; + } + } +} diff --git a/src/Application/Zipper.cs b/src/Application/Zipper.cs new file mode 100644 index 0000000..cc55fdc --- /dev/null +++ b/src/Application/Zipper.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; +using System.IO.Compression; + +namespace ZipArchiver.Application +{ + public class Zipper + { + private string dirName; + private string zipDirName; + private FileStream zipFile; + private ZipArchive archive; + + public delegate void IEventHandler(object sender, T args); + public event IEventHandler onException; + public event IEventHandler onCompressFile; + public event IEventHandler onProgress; + public event IEventHandler onUpdateLevel; + + private CompressionLevel compressionLevel; + private List excludeDirs; + + public Zipper(string dirName, string zipDirName) + { + this.dirName = dirName; + this.zipDirName = zipDirName; + this.compressionLevel = CompressionLevel.Fastest; + this.excludeDirs = new List(); + if (!Directory.Exists(dirName)) + { + throw new ArgumentException("Directory not exists."); + } + + if (!Directory.Exists(zipDirName)) + { + IOHelper.CreateDirectory(zipDirName); + } + } + + public void UpdateCompressionLevel(CompressionLevel level) + { + this.compressionLevel = level; + onUpdateLevel?.Invoke(this, level); + } + + public void AddExcludeDir(string dirPath) + { + this.excludeDirs.Add(new DirectoryInfo(dirPath)); + } + + public void CreateZip() + { + string zipPath = Path.Combine(zipDirName, DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".zip"); + if (File.Exists(zipPath)) + { + throw new Exception("ZipArchive already created!"); + } + + this.zipFile = new FileStream(zipPath, FileMode.Create); + this.archive = new ZipArchive(this.zipFile, ZipArchiveMode.Create); + + DirectoryInfo dirInfo = new DirectoryInfo(dirName); + AddEntry(dirInfo); + + this.archive.Dispose(); + this.zipFile.Dispose(); + } + + private void AddEntry(DirectoryInfo directory) + { + foreach (FileInfo file in directory.EnumerateFiles()) + { + AddEntry(file); + } + + foreach (DirectoryInfo dirInfo in directory.EnumerateDirectories()) + { + AddEntry(dirInfo); + } + } + + private void AddEntry(FileInfo file) + { + bool contains = this.excludeDirs.Any(dir => + { + return file.Directory.FullName.StartsWith(dir.FullName); + }); + if (contains) + { + return; + } + + try + { + onCompressFile?.Invoke(this, file); + + string internalPath = IOHelper.GetRelatedPath(this.dirName, file.FullName); + + ZipArchiveEntry entry = this.archive.CreateEntryFromFile(file.FullName, internalPath, this.compressionLevel); + + onProgress?.Invoke(this, entry); + } catch (IOException ex) + { + onException?.Invoke(this, ex); + } + } + } +} diff --git a/src/Application/ZipperConfig.cs b/src/Application/ZipperConfig.cs new file mode 100644 index 0000000..e30b353 --- /dev/null +++ b/src/Application/ZipperConfig.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ZipArchiver.Application +{ + public class ZipperConfig + { + public string TargetDirectoryName { get; set; } = "C:\\Target"; + public string ZipperDirectoryName { get; set; } = "C:\\Output"; + public bool EnableCommandLine { get; set; } = true; + public int CompressionLevel { get; set; } = 1; + public List ExcludesDirectory { get; set; } = new List(); + } +} diff --git a/src/Database/FileDatabase.cs b/src/Database/FileDatabase.cs new file mode 100644 index 0000000..d7a4a34 --- /dev/null +++ b/src/Database/FileDatabase.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; +using System.Xml; +using System.Xml.Serialization; +using ZipArchiver.Application; + +namespace ZipArchiver.Database +{ + public class FileDatabase : IDatabase + { + private string fileName; + public FileDatabase(string fileName) + { + this.fileName = fileName; + + if (!File.Exists(fileName)) + { + Write(default(ZipperConfig)); + } + } + + public ZipperConfig Read() + { + if (!File.Exists(fileName)) + { + return default(ZipperConfig); + } + + try + { + XmlSerializer serializer = new XmlSerializer(typeof(ZipperConfig)); + using (StreamReader reader = new StreamReader(fileName, new UTF8Encoding(false))) + { + return (ZipperConfig)serializer.Deserialize(reader); + } + } catch (IOException e) + { + Console.Write(e); + return null; + } + } + + public void Write(ZipperConfig config) + { + try + { + XmlSerializer serializer = new XmlSerializer(typeof(ZipperConfig)); + using (StreamWriter writer = new StreamWriter(fileName, false, new UTF8Encoding(false))) + { + serializer.Serialize(writer, config); + } + } catch (IOException) + { + } + } + } +} diff --git a/src/Program.cs b/src/Program.cs new file mode 100644 index 0000000..05c7b7b --- /dev/null +++ b/src/Program.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ZipArchiver.Application; +using ZipArchiver.Database; + +namespace ZipArchiver +{ + public class Program + { + public static void Main() + { + string appConfig = IOHelper.GetAppFile("zipper.xml"); + FileDatabase database = new FileDatabase(appConfig); + ZipperConfig config = database.Read(); + + if (config == null) + { + config = new ZipperConfig(); + config.ExcludesDirectory.Add(@"C:\"); + database.Write(config); + } + + string targetDirectoryName = config.TargetDirectoryName; + string zipperDirectoryName = config.ZipperDirectoryName; + + if (config.EnableCommandLine) + { + CommandLineHelper cmdHelper = new CommandLineHelper(); + if (cmdHelper.HasValue("input")) + { + targetDirectoryName = cmdHelper.GetValue("input"); + } + + if (cmdHelper.HasValue("output")) + { + zipperDirectoryName = cmdHelper.GetValue("output"); + } + } + + try + { + long fileCount = IOHelper.GetFileCount(targetDirectoryName, config.ExcludesDirectory); + + Console.WriteLine("Archive {0} files.", fileCount); + + Zipper zipper = new Zipper(targetDirectoryName, zipperDirectoryName); + + zipper.onException += (sender, e) => + { + Console.WriteLine("[Exception] {0}", e.Message); + Console.WriteLine("{0}", e.StackTrace); + }; + + zipper.onUpdateLevel += (sender, level) => + { + Console.WriteLine("[Level] Update Compression Level: {0}", level); + }; + + zipper.onCompressFile += (sender, file) => + { + string internalPath = IOHelper.GetRelatedPath(targetDirectoryName, file.FullName); + Console.WriteLine("[ZIP] Create Entry: {0}", internalPath); + }; + + foreach (string path in config.ExcludesDirectory) + { + zipper.AddExcludeDir(path); + } + + zipper.UpdateCompressionLevel((System.IO.Compression.CompressionLevel)config.CompressionLevel); + zipper.CreateZip(); + } catch (Exception ex) + { + Console.WriteLine(ex); + } + } + } +} diff --git a/src/ZipArchiver.csproj b/src/ZipArchiver.csproj new file mode 100644 index 0000000..48920ac --- /dev/null +++ b/src/ZipArchiver.csproj @@ -0,0 +1,27 @@ + + + + Exe + net6.0 + enable + disable + DevRas + DevRas + + + + + True + True + Resources.resx + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + diff --git a/src/ZipArchiver.sln b/src/ZipArchiver.sln new file mode 100644 index 0000000..0ef63af --- /dev/null +++ b/src/ZipArchiver.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZipArchiver", "ZipArchiver.csproj", "{06776A23-3B9E-4DF6-A7A2-3CA97F4790CB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {06776A23-3B9E-4DF6-A7A2-3CA97F4790CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {06776A23-3B9E-4DF6-A7A2-3CA97F4790CB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {06776A23-3B9E-4DF6-A7A2-3CA97F4790CB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {06776A23-3B9E-4DF6-A7A2-3CA97F4790CB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E733284E-418E-4B7B-BAC4-38B959960229} + EndGlobalSection +EndGlobal