Skip to content

Commit b62cef0

Browse files
add ScpProvider and update nuget packages
1 parent 523fe30 commit b62cef0

4 files changed

Lines changed: 163 additions & 3 deletions

File tree

FileSyncLibNet/FileSyncJob/FileSyncJob.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ private FileSyncJob(IFileJobOptions fileSyncJobOptions)
3535
case SyncProvider.Robocopy:
3636
syncProvider = new RoboCopyProvider(fileSyncJobOptions);
3737
break;
38+
case SyncProvider.SCP:
39+
syncProvider = new ScpProvider(fileSyncJobOptions);
40+
break;
3841
}
3942
}
4043

FileSyncLibNet/FileSyncLibNet.csproj

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
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="7.0.1" />
16-
<PackageReference Include="RoboSharp" Version="1.3.5" />
17-
<PackageReference Include="SMBLibrary" Version="1.5.0.1" />
15+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
16+
<PackageReference Include="RoboSharp" Version="1.5.3" />
17+
<PackageReference Include="SMBLibrary" Version="1.5.3.5" />
18+
<PackageReference Include="SSH.NET" Version="2024.1.0" />
1819
</ItemGroup>
1920
<ItemGroup>
2021
<Content Include="../README.md">
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
using FileSyncLibNet.Commons;
2+
using FileSyncLibNet.FileCleanJob;
3+
using FileSyncLibNet.FileSyncJob;
4+
using Microsoft.Extensions.Logging;
5+
using Renci.SshNet;
6+
using Renci.SshNet.Sftp;
7+
using System;
8+
using System.Diagnostics;
9+
using System.IO;
10+
using System.Linq;
11+
using System.Text.RegularExpressions;
12+
13+
namespace FileSyncLibNet.SyncProviders
14+
{
15+
internal class ScpProvider : ProviderBase
16+
{
17+
18+
19+
public ScpProvider(IFileJobOptions options) : base(options)
20+
{
21+
22+
}
23+
24+
private void CreateDirectoryRecursive(SftpClient client, string path)
25+
{
26+
var dirs = path.Split('/');
27+
string currentPath =path.StartsWith("/")?"/":"";
28+
for (int i = 0; i < dirs.Length; i++)
29+
{
30+
currentPath = (currentPath.EndsWith("/")?currentPath:(currentPath+"/")) + dirs[i];
31+
if (!string.IsNullOrEmpty(currentPath) && currentPath != "/")
32+
if(!client.Exists(currentPath))
33+
client.CreateDirectory(currentPath);
34+
}
35+
}
36+
37+
public override void SyncSourceToDest()
38+
{
39+
if (!(JobOptions is IFileSyncJobOptions jobOptions))
40+
throw new ArgumentException("this instance has no information about syncing files, it has type " + JobOptions.GetType().ToString());
41+
var sw = Stopwatch.StartNew();
42+
//Format
43+
44+
var pattern = @"scp://(?:(?<user>[^@]+)@)?(?<host>[^:/]+)(?::(?<port>\d+))?(?<path>/.*)?";
45+
var match = Regex.Match(JobOptions.DestinationPath, pattern);
46+
string path;
47+
SftpClient ftpClient;
48+
if (!match.Success)
49+
{
50+
throw new UriFormatException($"Unable to match scp pattern with given URL {JobOptions.DestinationPath}, use format scp://host:port/path");
51+
}
52+
else
53+
{
54+
var user = match.Groups["user"].Value;
55+
var host = match.Groups["host"].Value;
56+
var port = int.Parse(match.Groups["port"].Success ? match.Groups["port"].Value : "22"); // Default SCP port
57+
path = match.Groups["path"].Value;
58+
59+
60+
ftpClient = new SftpClient(host, port, JobOptions.Credentials.UserName, JobOptions.Credentials.Password);
61+
}
62+
ftpClient.Connect();
63+
CreateDirectoryRecursive(ftpClient, path);
64+
65+
var remoteFiles = ftpClient.ListDirectory(path);
66+
67+
Directory.CreateDirectory(jobOptions.SourcePath);
68+
69+
int copied = 0;
70+
int skipped = 0;
71+
DirectoryInfo _di = new DirectoryInfo(jobOptions.SourcePath);
72+
//Dateien ins Backup kopieren
73+
if (JobOptions.Credentials != null)
74+
{
75+
76+
}
77+
78+
79+
foreach (var dir in JobOptions.Subfolders.Count > 0 ? _di.GetDirectories() : new[] { _di })
80+
{
81+
if (JobOptions.Subfolders.Count > 0 && !JobOptions.Subfolders.Select(x => x.ToLower()).Contains(dir.Name.ToLower()))
82+
continue;
83+
var _fi = dir.EnumerateFiles(
84+
searchPattern: JobOptions.SearchPattern,
85+
searchOption: JobOptions.Recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
86+
87+
if (jobOptions.RememberLastSync)
88+
{
89+
var old = _fi.Count();
90+
_fi = _fi.Where(x => x.LastWriteTime > (LastRun - jobOptions.Interval)).ToList();
91+
skipped += old - _fi.Count();
92+
LastRun = DateTimeOffset.Now;
93+
}
94+
foreach (FileInfo f in _fi)
95+
{
96+
97+
var relativeFilename = f.FullName.Substring(Path.GetFullPath(jobOptions.SourcePath).Length).TrimStart('\\');
98+
var remoteFilename = Path.Combine(path, relativeFilename).Replace('\\', '/');
99+
ISftpFile remotefile = null;
100+
if (ftpClient.Exists(remoteFilename))
101+
{
102+
remotefile = ftpClient.Get(remoteFilename);
103+
}
104+
bool exists = remotefile != null;
105+
bool lengthMatch = exists && remotefile.Length == f.Length;
106+
bool timeMatch = exists && Math.Abs((remotefile.LastWriteTimeUtc -f.LastWriteTimeUtc).TotalSeconds)<1;
107+
108+
bool copy = !exists || !lengthMatch || !timeMatch;
109+
if (copy)
110+
{
111+
try
112+
{
113+
logger.LogDebug("Copy {A}", relativeFilename);
114+
CreateDirectoryRecursive(ftpClient, Path.GetDirectoryName(remoteFilename).Replace('\\', '/'));
115+
116+
using (var fileStream = System.IO.File.OpenRead(f.FullName))
117+
{
118+
ftpClient.UploadFile(fileStream, remoteFilename);
119+
}
120+
ftpClient.SetLastWriteTimeUtc(remoteFilename, f.LastAccessTimeUtc);
121+
copied++;
122+
if (jobOptions.DeleteSourceAfterBackup)
123+
{
124+
File.Delete(f.FullName);
125+
}
126+
}
127+
catch (Exception exc)
128+
{
129+
logger.LogError(exc, "Exception copying {A}", relativeFilename);
130+
}
131+
}
132+
else
133+
{
134+
skipped++;
135+
logger.LogTrace("Skip {A}", relativeFilename);
136+
}
137+
}
138+
}
139+
sw.Stop();
140+
logger.LogInformation("{A} files copied, {B} files skipped in {C}s", copied, skipped, sw.ElapsedMilliseconds / 1000.0);
141+
}
142+
143+
144+
public override void DeleteFiles()
145+
{
146+
if (!(JobOptions is IFileCleanJobOptions jobOptions))
147+
throw new ArgumentException("this instance has no information about deleting files, it has type " + JobOptions.GetType().ToString());
148+
149+
throw new NotImplementedException("deleting files via scp currently is not supported");
150+
}
151+
152+
153+
154+
}
155+
}

FileSyncLibNet/SyncProviders/SyncProvider.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ public enum SyncProvider
99
FileIO,
1010
SMBLib,
1111
Robocopy,
12+
SCP,
1213
}
1314
}

0 commit comments

Comments
 (0)