Skip to content

Commit fbef91e

Browse files
Merge pull request #14 from andreaskueffel/development
add abstract provider to be more flexible in syncing
2 parents 5f4fa1f + 8823fcf commit fbef91e

File tree

10 files changed

+411
-18
lines changed

10 files changed

+411
-18
lines changed

FileSyncApp/FileSyncApp.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77

88
<ItemGroup>
99
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
10-
<PackageReference Include="Serilog" Version="3.0.1" />
11-
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
12-
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
13-
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
14-
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
10+
<PackageReference Include="Serilog" Version="4.1.0" />
11+
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
12+
<PackageReference Include="Serilog.Sinks.Async" Version="2.1.0" />
13+
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
14+
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
1515
<PackageReference Include="System.Runtime.InteropServices" Version="4.3.0" />
1616
</ItemGroup>
1717

FileSyncApp/Program.cs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,15 @@
11
using FileSyncLibNet.Commons;
22
using FileSyncLibNet.FileCleanJob;
33
using FileSyncLibNet.FileSyncJob;
4-
using FileSyncLibNet.Logger;
54
using FileSyncLibNet.SyncProviders;
65
using Microsoft.Extensions.Logging;
76
using Newtonsoft.Json;
87
using Serilog;
98
using Serilog.Core;
109
using System;
1110
using System.Collections.Generic;
12-
using System.Diagnostics;
13-
using System.IO;
1411
using System.Linq;
1512
using System.Net;
16-
using System.Reflection;
17-
using System.Runtime.InteropServices;
18-
using System.Runtime.InteropServices.ComTypes;
19-
using System.Security.Cryptography.X509Certificates;
20-
using System.Text;
2113
using System.Threading;
2214
using File = System.IO.File;
2315

@@ -35,7 +27,7 @@ public static void Main(string[] args)
3527
{
3628
ConfigureLogger();
3729
log = LoggerFactory.CreateLogger("FileSyncAppMain");
38-
if (null!=args && args.Length > 0)
30+
if (null != args && args.Length > 0)
3931
{
4032
if (args.Contains("debug"))
4133
{
@@ -46,8 +38,8 @@ public static void Main(string[] args)
4638
RunProgram();
4739

4840
}
49-
50-
41+
42+
5143
static void RunProgram()
5244
{
5345
log.LogInformation("FileSyncApp - synchronizing folders and clean them up");
@@ -103,7 +95,7 @@ static void RunProgram()
10395
foreach (var jobOption in readJobOptions)
10496
{
10597

106-
jobOption.Value.Logger = LoggerFactory.CreateLogger(jobOption.Key);
98+
jobOption.Value.Logger = LoggerFactory.CreateLogger(jobOption.Key);
10799
Jobs.Add(jobOption.Key, FileSyncJob.CreateJob(jobOption.Value));
108100
}
109101
JobsReady?.Invoke(null, EventArgs.Empty);
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using FileSyncLibNet.Commons;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.IO;
5+
using System.Linq;
6+
7+
namespace FileSyncLibNet.AccessProviders
8+
{
9+
internal class FileIoAccessProvider : IAccessProvider
10+
{
11+
public string AccessPath { get; }
12+
public FileIoAccessProvider(string accessPath)
13+
{
14+
AccessPath = accessPath;
15+
}
16+
17+
public void CreateDirectory(string path)
18+
{
19+
Directory.CreateDirectory(Path.Combine(AccessPath, path));
20+
}
21+
22+
public FileInfo2 GetFileInfo(string path)
23+
{
24+
var fi = new FileInfo(Path.Combine(AccessPath, path));
25+
26+
return new FileInfo2(path, fi.Exists)
27+
{
28+
LastWriteTime = fi.Exists ? fi.LastWriteTime : DateTime.MinValue,
29+
Length = fi.Exists ? fi.Length : 0
30+
};
31+
}
32+
33+
public List<FileInfo2> GetFiles(DateTime minimumLastWriteTime, string pattern, string path = null, bool recursive = false, List<string> subfolders = null)
34+
{
35+
DirectoryInfo _di = new DirectoryInfo(AccessPath);
36+
List<FileInfo2> ret_val = new List<FileInfo2>();
37+
foreach (var dir in (subfolders != null && subfolders.Count > 0) ? _di.GetDirectories() : new[] { _di })
38+
{
39+
if (subfolders != null && subfolders.Count > 0 && !subfolders.Select(x => x.ToLower()).Contains(dir.Name.ToLower()))
40+
continue;
41+
var _fi = dir.EnumerateFiles(
42+
searchPattern: pattern,
43+
searchOption: recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
44+
45+
46+
ret_val.AddRange(_fi.Select(x => new FileInfo2(x.FullName.Substring(AccessPath.Length + 1), x.Exists) { Length = x.Length, LastWriteTime = x.LastWriteTime }).ToList());
47+
}
48+
return ret_val;
49+
}
50+
51+
public Stream GetStream(FileInfo2 file)
52+
{
53+
var realFilename = Path.Combine(AccessPath, file.Name);
54+
return File.OpenRead(realFilename);
55+
}
56+
57+
public void WriteFile(FileInfo2 file, Stream content)
58+
{
59+
var realFilename = Path.Combine(AccessPath, file.Name);
60+
Directory.CreateDirectory(Path.GetDirectoryName(realFilename));
61+
using (var stream = File.Create(realFilename))
62+
{
63+
content.CopyTo(stream);
64+
}
65+
File.SetLastWriteTime(realFilename, file.LastWriteTime);
66+
67+
}
68+
public void Delete(FileInfo2 fileInfo)
69+
{
70+
var realFilename = Path.Combine(AccessPath, fileInfo.Name);
71+
File.Delete(realFilename);
72+
}
73+
}
74+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using FileSyncLibNet.Commons;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.IO;
5+
6+
namespace FileSyncLibNet.AccessProviders
7+
{
8+
internal interface IAccessProvider
9+
{
10+
void CreateDirectory(string path);
11+
List<FileInfo2> GetFiles(DateTime minimumLastWriteTime, string pattern, string path = null, bool recursive = false, List<string> subfolders = null);
12+
FileInfo2 GetFileInfo(string path);
13+
void Delete(FileInfo2 fileInfo);
14+
Stream GetStream(FileInfo2 file);
15+
void WriteFile(FileInfo2 file, Stream content);
16+
}
17+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
using FileSyncLibNet.Commons;
2+
using Renci.SshNet;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.IO;
6+
using System.Linq;
7+
using System.Net;
8+
using System.Text.RegularExpressions;
9+
10+
namespace FileSyncLibNet.AccessProviders
11+
{
12+
internal class ScpAccessProvider : IAccessProvider
13+
{
14+
string AccessPathUri { get; }
15+
NetworkCredential Credentials { get; }
16+
SftpClient ftpClient;
17+
public string AccessPath { get; private set; }
18+
19+
public ScpAccessProvider(string accessPath, NetworkCredential credentials)
20+
{
21+
AccessPathUri = accessPath;
22+
Credentials = credentials;
23+
//CreateClient();
24+
}
25+
void CreateClient()
26+
{
27+
var pattern = @"scp://(?:(?<user>[^@]+)@)?(?<host>[^:/]+)(?::(?<port>\d+))?(?<path>/.*)?";
28+
var match = Regex.Match(AccessPathUri, pattern);
29+
30+
31+
if (!match.Success)
32+
{
33+
throw new UriFormatException($"Unable to match scp pattern with given URL {AccessPathUri}, use format scp://host:port/path");
34+
}
35+
else
36+
{
37+
var user = match.Groups["user"].Value;
38+
var host = match.Groups["host"].Value;
39+
var port = int.Parse(match.Groups["port"].Success ? match.Groups["port"].Value : "22"); // Default SCP port
40+
AccessPath = match.Groups["path"].Value;
41+
if (AccessPath.StartsWith("/~"))
42+
AccessPath = AccessPath.Substring(1);
43+
44+
ftpClient = new SftpClient(host, port, Credentials.UserName, Credentials.Password);
45+
}
46+
}
47+
void EnsureConnected()
48+
{
49+
if (ftpClient == null)
50+
{
51+
CreateClient();
52+
}
53+
if (!ftpClient.IsConnected)
54+
{
55+
ftpClient.Connect();
56+
}
57+
}
58+
private void CreateDirectoryRecursive(SftpClient client, string path)
59+
{
60+
var dirs = path.Split('/');
61+
string currentPath = path.StartsWith("/") ? "/" : /*path.StartsWith("~") ? "~/" :*/ "";
62+
for (int i = 0; i < dirs.Length; i++)
63+
{
64+
if (dirs[i] == "~")
65+
continue;
66+
currentPath = (currentPath.EndsWith("/") ? currentPath : currentPath == "" ? dirs[i] : ((currentPath + "/")) + dirs[i]);
67+
if (!string.IsNullOrEmpty(currentPath) && currentPath != "/" && currentPath != "/~")
68+
if (!client.Exists(currentPath))
69+
client.CreateDirectory(currentPath);
70+
}
71+
}
72+
73+
74+
public void CreateDirectory(string path)
75+
{
76+
EnsureConnected();
77+
var realDir = System.IO.Path.Combine(AccessPath, path).Replace("\\", "/");
78+
CreateDirectoryRecursive(ftpClient, realDir);
79+
}
80+
81+
public FileInfo2 GetFileInfo(string path)
82+
{
83+
EnsureConnected();
84+
var realFilename = System.IO.Path.Combine(AccessPath, path).Replace("\\", "/");
85+
if (ftpClient.Exists(realFilename))
86+
{
87+
var fi = ftpClient.ListDirectory(realFilename);
88+
return new FileInfo2(path, true)
89+
{
90+
LastWriteTime = fi.First().LastWriteTime,
91+
Length = fi.First().Length
92+
};
93+
}
94+
else
95+
{
96+
return new FileInfo2(path, false)
97+
{
98+
LastWriteTime = DateTime.MinValue,
99+
Length = 0
100+
};
101+
}
102+
}
103+
104+
public bool MatchesPattern(string fileName, string pattern)
105+
{
106+
if (string.IsNullOrEmpty(pattern))
107+
return true;
108+
string regexPattern = "^" + Regex.Escape(pattern).Replace("\\*", ".*").Replace("\\?", ".") + "$";
109+
return Regex.IsMatch(fileName, regexPattern, RegexOptions.IgnoreCase);
110+
}
111+
112+
public List<FileInfo2> GetFiles(DateTime minimumLastWriteTime, string pattern, string path = null, bool recursive = false, List<string> subfolders = null)
113+
{
114+
EnsureConnected();
115+
string basePath = string.IsNullOrEmpty(path) ? AccessPath : path;
116+
List<FileInfo2> ret_val = new List<FileInfo2>();
117+
if (subfolders != null && subfolders.Count > 0)
118+
{
119+
foreach (string subfolder in subfolders)
120+
{
121+
var subPath = System.IO.Path.Combine(basePath, subfolder).Replace("\\", "/");
122+
ret_val.AddRange(GetFiles(minimumLastWriteTime, pattern, path: subPath, recursive: recursive));
123+
}
124+
}
125+
else
126+
{
127+
var files = ftpClient.ListDirectory(basePath);
128+
if (recursive)
129+
{
130+
foreach (var folder in files.Where(x => x.IsDirectory && !(x.Name == ".") && !(x.Name == "..")))
131+
{
132+
var subPath = System.IO.Path.Combine(folder.Name).Replace("\\", "/");
133+
ret_val.AddRange(GetFiles(minimumLastWriteTime, pattern, subPath, recursive));
134+
}
135+
}
136+
ret_val.AddRange(files.Where(x => MatchesPattern(x.Name, pattern)).Where(x => x.LastWriteTime >= minimumLastWriteTime && !x.IsDirectory).Select(x =>
137+
new FileInfo2($"{basePath.Substring(AccessPath.Length + 1)}/{x.Name}", exists: true) { LastWriteTime = x.LastWriteTime, Length = x.Length }).ToList());
138+
}
139+
return ret_val;
140+
}
141+
142+
public Stream GetStream(FileInfo2 file)
143+
{
144+
EnsureConnected();
145+
var subPath = System.IO.Path.Combine(AccessPath, file.Name).Replace("\\", "/");
146+
return ftpClient.OpenRead(subPath);
147+
}
148+
149+
public void WriteFile(FileInfo2 file, Stream content)
150+
{
151+
EnsureConnected();
152+
var filePath = System.IO.Path.Combine(AccessPath, file.Name).Replace("\\", "/");
153+
CreateDirectoryRecursive(ftpClient, filePath.Substring(0, filePath.LastIndexOf("/")));
154+
if (filePath.StartsWith("~/"))
155+
filePath = filePath.Substring(2);
156+
157+
using (var stream = ftpClient.Open(filePath, FileMode.Create))
158+
{
159+
160+
content.CopyTo(stream);
161+
}
162+
}
163+
164+
public void Delete(FileInfo2 fileInfo)
165+
{
166+
EnsureConnected();
167+
var filePath = System.IO.Path.Combine(AccessPath, fileInfo.Name).Replace("\\", "/");
168+
ftpClient.Delete(filePath);
169+
}
170+
}
171+
}

FileSyncLibNet/Commons/FileInfo2.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
3+
namespace FileSyncLibNet.Commons
4+
{
5+
internal class FileInfo2 //: FileSystemInfo
6+
{
7+
public FileInfo2(string name, bool exists = true)
8+
{
9+
Name = name;
10+
Exists = exists;
11+
}
12+
public long Length { get; set; }
13+
public DateTime LastWriteTime { get; set; }
14+
public bool Exists { get; }
15+
public string Name { get; }
16+
17+
public override string ToString()
18+
{
19+
return $"{Name}, {LastWriteTime}, Size {Length}";
20+
}
21+
22+
}
23+
}

FileSyncLibNet/FileSyncJob/FileSyncJob.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ private FileSyncJob(IFileJobOptions fileSyncJobOptions)
3838
case SyncProvider.SCP:
3939
syncProvider = new ScpProvider(fileSyncJobOptions);
4040
break;
41+
case SyncProvider.Abstract:
42+
syncProvider = new AbstractProvider(fileSyncJobOptions);
43+
break;
4144
}
4245
}
4346

FileSyncLibNet/FileSyncLibNet.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<Description>A library to easily backup or sync 2 folders either once or in a given interval.</Description>
1313
</PropertyGroup>
1414
<ItemGroup>
15-
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
15+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
1616
<PackageReference Include="RoboSharp" Version="1.5.3" />
1717
<PackageReference Include="SMBLibrary" Version="1.5.3.5" />
1818
<PackageReference Include="SSH.NET" Version="2024.1.0" />

0 commit comments

Comments
 (0)