From 74d2142e6f0fa7f1a16054df5ab2241fcdd12c51 Mon Sep 17 00:00:00 2001 From: sanjusss Date: Mon, 24 Dec 2018 17:30:59 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E5=A2=9E=E5=8A=A0FTP=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=BA=90=E3=80=82=202=E3=80=81=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E5=88=B0FTP=E3=80=82=203=E3=80=81=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E4=BE=8B=E5=AD=90=E8=B0=83=E7=94=A8=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Backup2Cloud/Args/ExampleOptions.cs | 11 +++ Backup2Cloud/Backup2Cloud.csproj | 15 ++- Backup2Cloud/Conf/BaseFtpConf.cs | 46 +++++++++ Backup2Cloud/Conf/ExamplePrinter.cs | 106 ++++++++++++++++---- Backup2Cloud/Conf/SingleConfiguration.cs | 2 +- Backup2Cloud/DataSource/FtpDataSource.cs | 117 +++++++++++++++++++++++ Backup2Cloud/Uploader/FtpUploader.cs | 50 ++++++++++ 7 files changed, 321 insertions(+), 26 deletions(-) create mode 100644 Backup2Cloud/Conf/BaseFtpConf.cs create mode 100644 Backup2Cloud/DataSource/FtpDataSource.cs create mode 100644 Backup2Cloud/Uploader/FtpUploader.cs diff --git a/Backup2Cloud/Args/ExampleOptions.cs b/Backup2Cloud/Args/ExampleOptions.cs index 53e5c1c..5d29157 100644 --- a/Backup2Cloud/Args/ExampleOptions.cs +++ b/Backup2Cloud/Args/ExampleOptions.cs @@ -7,5 +7,16 @@ public class ExampleOptions { [Option('s', "save", Required = false, Default = null, HelpText = "示例配置文件保存地址。")] public string Path { get; set; } + + [Option('l', "list", Required = false, Default = null, HelpText = "获取datasource或uploader的名称。可以为 datasource 或 uploader")] + + public string List { get; set; } + + [Option('d', "datasource", Required = false, Default = null, HelpText = "获取datasource的格式,需填写名称。")] + public string DataSource { get; set; } + + + [Option('u', "uploader", Required = false, Default = null, HelpText = "获取uploader的格式,需填写名称。")] + public string Uploader { get; set; } } } diff --git a/Backup2Cloud/Backup2Cloud.csproj b/Backup2Cloud/Backup2Cloud.csproj index 7458417..c16a27c 100644 --- a/Backup2Cloud/Backup2Cloud.csproj +++ b/Backup2Cloud/Backup2Cloud.csproj @@ -19,18 +19,23 @@ - + - + + - + - + - + + + + + diff --git a/Backup2Cloud/Conf/BaseFtpConf.cs b/Backup2Cloud/Conf/BaseFtpConf.cs new file mode 100644 index 0000000..f1224a7 --- /dev/null +++ b/Backup2Cloud/Conf/BaseFtpConf.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Backup2Cloud.Conf +{ + /// + /// FTP基本配置。 + /// + public abstract class BaseFtpConf + { + /// + /// 服务器主机名。 + /// 默认为本机。 + /// + public string host { get; set; } = "localhost"; + + /// + /// 服务器的FTP端口。 + /// 默认为21。 + /// + public int port { get; set; } = 21; + + /// + /// 用户名。 + /// 默认为anonymous。 + /// + public string user { get; set; } = "anonymous"; + + /// + /// 密码。 + /// + public string password { get; set; } = string.Empty; + + /// + /// 是否使用匿名登陆。 + /// 默认为false。 + /// + public bool anonymous { get; set; } = false; + + /// + /// 在ftp上的路径。 + /// + public string path { get; set; } = "/"; + } +} diff --git a/Backup2Cloud/Conf/ExamplePrinter.cs b/Backup2Cloud/Conf/ExamplePrinter.cs index 782fd23..2353489 100644 --- a/Backup2Cloud/Conf/ExamplePrinter.cs +++ b/Backup2Cloud/Conf/ExamplePrinter.cs @@ -1,4 +1,5 @@ using Backup2Cloud.Args; +using Backup2Cloud.DataSource; using Backup2Cloud.Logging; using Backup2Cloud.Uploader; using Newtonsoft.Json; @@ -21,38 +22,103 @@ public class ExamplePrinter public static void Print(ExampleOptions options) { var uploaders = NamedInterfaceLoader.Load(typeof(IUploader)); - List configurations = new List(); - string[] crontab = { "0,30 * * * * ?" }; - foreach (var i in uploaders) + var dataSources = NamedInterfaceLoader.Load(typeof(IDataSource)); + string content = string.Empty; + if (string.IsNullOrEmpty(options.List) == false) { - SingleConfiguration single = new SingleConfiguration() + if (options.List == "datasource") { - uploader = (Activator.CreateInstance(i.Value) as IUploader).GetExample() as IUploader, - name = "上传到 " + i.Key, - path = "/data", - crontab = new HashSet(crontab) - }; - - configurations.Add(single); + content = string.Join('\n', dataSources.Keys); + Console.WriteLine("已支持以下数据源:"); + } + else if (options.List == "uploader") + { + content = string.Join('\n', uploaders.Keys); + Console.WriteLine("已支持以下上传类:"); + } + else + { + Console.WriteLine("只能列出 datasource 或 uploader"); + } } - - string json = JsonConvert.SerializeObject(configurations, Formatting.Indented, new NameConverter()); - Log.Info("\n\n示例文件:\n"); - Console.WriteLine(json); - if (string.IsNullOrEmpty(options.Path) == false) + else if (string.IsNullOrEmpty(options.Uploader) == false) { - //保存到文件。 - using (StreamWriter stream = new StreamWriter(options.Path, false, Encoding.UTF8)) + if (uploaders.ContainsKey(options.Uploader)) { - stream.Write(json); + content = JsonConvert.SerializeObject((Activator.CreateInstance(uploaders[options.Uploader]) as IExampled).GetExample(), + Formatting.Indented, + new NameConverter()); } + else + { + Console.WriteLine($"不存在uploader类: { options.Uploader }"); + } + } + else if (string.IsNullOrEmpty(options.DataSource) == false) + { + if (dataSources.ContainsKey(options.DataSource)) + { + content = JsonConvert.SerializeObject((Activator.CreateInstance(dataSources[options.DataSource]) as IExampled).GetExample(), + Formatting.Indented, + new NameConverter()); + } + else + { + Console.WriteLine($"不存在datasource类: { options.DataSource }"); + } + } + else + { + content = GetExampleConfig(uploaders, dataSources); + } - Log.Info(string.Format("示例文件已经保存到\"{0}\"。", options.Path)); + if (string.IsNullOrEmpty(content) == false) + { + Console.WriteLine(content); + if (string.IsNullOrEmpty(options.Path) == false) + { + //保存到文件。 + using (StreamWriter stream = new StreamWriter(options.Path, false, Encoding.UTF8)) + { + stream.Write(content); + } + + Console.WriteLine("\n\n"); + Log.Info(string.Format("文件已经保存到\"{0}\"。", options.Path)); + } } #if !DEBUG Environment.Exit(0); #endif } + + private static string GetExampleConfig(Dictionary uploaders, Dictionary dataSources) + { + string[] crontab = { "0,30 * * * * ?" }; + List configurations = new List(); + SingleConfiguration single = new SingleConfiguration() + { + name = "上传到 ", + path = "/data", + crontab = new HashSet(crontab) + }; + + if (uploaders.Count > 0) + { + var i = uploaders.GetEnumerator().Current; + single.uploader = (Activator.CreateInstance(i.Value) as IUploader).GetExample() as IUploader; + single.name += i.Key; + } + + if (dataSources.Count > 0) + { + var i = dataSources.GetEnumerator().Current; + single.dataSource = (Activator.CreateInstance(i.Value) as IDataSource).GetExample() as IDataSource; + } + + configurations.Add(single); + return JsonConvert.SerializeObject(configurations, Formatting.Indented, new NameConverter()); + } } } diff --git a/Backup2Cloud/Conf/SingleConfiguration.cs b/Backup2Cloud/Conf/SingleConfiguration.cs index a7d6c11..0d4094c 100644 --- a/Backup2Cloud/Conf/SingleConfiguration.cs +++ b/Backup2Cloud/Conf/SingleConfiguration.cs @@ -7,7 +7,7 @@ namespace Backup2Cloud.Conf /// /// 单条备份配置 /// - public struct SingleConfiguration : IConfigurable + public class SingleConfiguration : IConfigurable { /// /// 任务名称 diff --git a/Backup2Cloud/DataSource/FtpDataSource.cs b/Backup2Cloud/DataSource/FtpDataSource.cs new file mode 100644 index 0000000..45d93b7 --- /dev/null +++ b/Backup2Cloud/DataSource/FtpDataSource.cs @@ -0,0 +1,117 @@ +using Backup2Cloud.Conf; +using Backup2Cloud.Logging; +using FluentFTP; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; + +namespace Backup2Cloud.DataSource +{ + /// + /// FTP数据源。从远端FTP获取数据。 + /// + [Name("ftp")] + public class FtpDataSource : BaseFtpConf, IDataSource + { + public string Tips + { + get + { + return string.Empty; + } + } + + public object GetExample() + { + return new FtpDataSource(); + } + + public void SaveData(string des) + { + using (FtpClient client = new FtpClient(host)) + { + client.Port = port; + client.Encoding = Encoding.UTF8; + if (anonymous == false) + { + client.Credentials = new NetworkCredential(user, password); + } + + client.Connect(); + if (client.DirectoryExists(path)) + { + int result = DownloadDirectory(path, "/", des, client); + Log.Print($"从 ftp://{ host }:{ port }{ path } 成功下载 { result } 个文件到 { des } 。"); + } + else + { + if (client.DownloadFile(des, path) == false) + { + throw new IOException($"下载 ftp://{ host }:{ port }{ path } 失败。"); + } + + Log.Print($"成功下载 ftp://{ host }:{ port }{ path } 到 { des } 。"); + } + + client.Disconnect(); + } + } + + private int DownloadDirectory(string baseSrc, string dir, string des, FtpClient client) + { + string target = FormatDirectoryName(des + dir); + if (Directory.Exists(target)) + { + Directory.Delete(target, true); + } + + Directory.CreateDirectory(target); + var list = client.GetListing(FormatDirectoryName(baseSrc + dir)); + List dirs = new List(); + int result = 0; + foreach (var i in list) + { + switch (i.Type) + { + case FtpFileSystemObjectType.File: + if (client.DownloadFile(target + i.Name, i.FullName)) + { + ++result; + } + + break; + + case FtpFileSystemObjectType.Directory: + dirs.Add(i.Name); + break; + + case FtpFileSystemObjectType.Link: + break; + + default: + break; + } + } + + foreach (var i in dirs) + { + result += DownloadDirectory(baseSrc, dir + i + "/", des + i + "/", client); + } + + return result; + } + + private string FormatDirectoryName(string name) + { + name = name.Replace("\\", "/"); + name = name.Replace("//", "/"); + if (name.EndsWith("/") == false) + { + name += "/"; + } + + return name; + } + } +} diff --git a/Backup2Cloud/Uploader/FtpUploader.cs b/Backup2Cloud/Uploader/FtpUploader.cs new file mode 100644 index 0000000..67cbdc4 --- /dev/null +++ b/Backup2Cloud/Uploader/FtpUploader.cs @@ -0,0 +1,50 @@ +using Backup2Cloud.Conf; +using FluentFTP; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace Backup2Cloud.Uploader +{ + public class FtpUploader : BaseFtpConf, IUploader + { + public string Tips + { + get + { + return string.Empty; + } + } + + public object GetExample() + { + return new FtpUploader(); + } + + public Task Upload(string file, string suffix) + { + using (FtpClient client = new FtpClient(host)) + { + client.Port = port; + client.Encoding = Encoding.UTF8; + if (anonymous == false) + { + client.Credentials = new NetworkCredential(user, password); + } + + client.Connect(); + if (client.UploadFile(file, path + suffix) == false) + { + throw new IOException($"上传 ftp://{ host }:{ port }{ path }{ suffix } 失败。"); + } + + client.Disconnect(); + } + + return Task.CompletedTask; + } + } +}