first commit

This commit is contained in:
Sakurai Ryota 2024-03-23 17:50:56 +09:00
commit 687b68db31
12 changed files with 526 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
bin
obj
.vscode
*.user
Properties

21
LICENSE Normal file
View File

@ -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.

37
README.md Normal file
View File

@ -0,0 +1,37 @@
ZipArchiver
===
設定ファイルで指定されたフォルダからZIPファイルを作成するプログラム。
## 動作環境
.NET6 SDK
### 動作確認済みの環境
- Windows11
- Debian11
## 設定ファイルの例
XML形式で指定します。
フォルダパスについては、環境に応じて、変更してください。
```xml
<?xml version="1.0" encoding="utf-8"?>
<ZipperConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<!-- ZIP化する対象のフォルダパス -->
<TargetDirectoryName>D:\DataFolder</TargetDirectoryName>
<!-- ZIPファイルを出力するフォルダパス -->
<ZipperDirectoryName>D:\Backups</ZipperDirectoryName>
<!-- コマンドラインから入出力先を上書きできるようにするか -->
<EnableCommandLine>true</EnableCommandLine>
<!-- 圧縮のレベル -->
<!-- レベルは`System.IO.Compression.CompressionLevel`に依存する。 -->
<CompressionLevel>1</CompressionLevel>
<!-- ZIPファイルに含めないフォルダパス(絶対パスで指定) -->
<ExcludesDirectory>
<string>D:\DataFolder\ZIPに含めない</string>
<string>D:\DataFolder\ZIPに含めない</string>
</ExcludesDirectory>
</ZipperConfig>
```

View File

@ -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<string, string> _commands = new Dictionary<string, string>();
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);
}
}
}
}

View File

@ -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);
}
}

View File

@ -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<string> 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;
}
}
}

112
src/Application/Zipper.cs Normal file
View File

@ -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<T>(object sender, T args);
public event IEventHandler<Exception> onException;
public event IEventHandler<FileInfo> onCompressFile;
public event IEventHandler<ZipArchiveEntry> onProgress;
public event IEventHandler<CompressionLevel> onUpdateLevel;
private CompressionLevel compressionLevel;
private List<DirectoryInfo> excludeDirs;
public Zipper(string dirName, string zipDirName)
{
this.dirName = dirName;
this.zipDirName = zipDirName;
this.compressionLevel = CompressionLevel.Fastest;
this.excludeDirs = new List<DirectoryInfo>();
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);
}
}
}
}

View File

@ -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<string> ExcludesDirectory { get; set; } = new List<string>();
}
}

View File

@ -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)
{
}
}
}
}

81
src/Program.cs Normal file
View File

@ -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);
}
}
}
}

27
src/ZipArchiver.csproj Normal file
View File

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<Company>DevRas</Company>
<Authors>DevRas</Authors>
</PropertyGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

25
src/ZipArchiver.sln Normal file
View File

@ -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