@@ -33,25 +33,25 @@ public RemoteSync(string host, string username, string password,
3333 _username = username ;
3434 _password = password ;
3535 _searchPattern = searchPattern ;
36- _localRootDirectory = localRootDirectory ;
37- _remoteRootDirectory = remoteRootDirectory ;
36+ _localRootDirectory = Path . TrimEndingDirectorySeparator ( Path . GetFullPath ( localRootDirectory ) ) ;
37+ _remoteRootDirectory = remoteRootDirectory . TrimEnd ( '/' , ' \\ ' ) ;
3838 _director = director ;
3939 _excludedFolders = excludedFolders ?? new List < string > ( ) ;
4040 _sftp = new SftpClient ( host , username , password ) ;
41- _sftp . Connect ( ) ;
4241
43- //Our first instance is responsible for creating all of the the directories
44- //Subsequent instances will not be created until this is done
45- DoneMakingFolders = createFolders ? CreateDirectories ( _localRootDirectory , _remoteRootDirectory ) : Task . CompletedTask ;
42+ //The first instance is responsible for creating ALL of the the directories.
43+ //Subsequent instances will not be created until this one completes.
44+
45+ DoneMakingFolders = createFolders
46+ ? CreateDirectories ( _localRootDirectory , _remoteRootDirectory )
47+ : Task . CompletedTask ;
4648
4749 //Now perform the initial sync for the pattern this instance is responsible for
50+
4851 DoneInitialSync = InitialSync ( _localRootDirectory , _remoteRootDirectory ) ;
4952
50- //Once the initial sync is done, we can start watching the file system for changes
51- DoneInitialSync . ContinueWith ( ( tmp ) =>
52- {
53- _director . AddCallback ( searchPattern , ( args ) => Fsw_Changed ( null , args ) ) ;
54- } ) ;
53+ // Register callbacks immediately; handler will ignore events until initial sync completes.
54+ _director . AddCallback ( searchPattern , ( args ) => Fsw_Changed ( null , args ) ) ;
5555 }
5656
5757 public RemoteSync ( string host , string username , string password ,
@@ -62,31 +62,24 @@ public RemoteSync(string host, string username, string password,
6262 _username = username ;
6363 _password = password ;
6464 _searchPattern = searchPattern ;
65- _localRootDirectory = localRootDirectory ;
66- _remoteRootDirectory = remoteRootDirectory ;
65+ _localRootDirectory = Path . TrimEndingDirectorySeparator ( Path . GetFullPath ( localRootDirectory ) ) ;
66+ _remoteRootDirectory = remoteRootDirectory . TrimEnd ( '/' , ' \\ ' ) ;
6767 _director = director ;
6868 _excludedFolders = excludedFolders ?? new List < string > ( ) ;
6969 _sftp = new SftpClient ( host , username , password ) ;
70- _sftp . Connect ( ) ;
7170
7271 DoneMakingFolders = Task . CompletedTask ;
72+
7373 DoneInitialSync = initialSyncTask ;
7474
75- DoneInitialSync . ContinueWith ( ( tmp ) =>
76- {
77- _director . AddCallback ( searchPattern , ( args ) => Fsw_Changed ( null , args ) ) ;
78- } ) ;
75+ // Register callbacks immediately; handler will ignore events until initial sync completes.
76+ _director . AddCallback ( searchPattern , ( args ) => Fsw_Changed ( null , args ) ) ;
7977 }
8078
8179 public static async Task RunSharedInitialSyncAsync (
82- string host ,
83- string username ,
84- string password ,
85- string localRootDirectory ,
86- string remoteRootDirectory ,
87- string [ ] searchPatterns ,
88- List < string > ? excludedFolders ,
89- int workerCount )
80+ string host , string username , string password ,
81+ string localRootDirectory , string remoteRootDirectory ,
82+ string [ ] searchPatterns , List < string > ? excludedFolders , int workerCount )
9083 {
9184 if ( workerCount <= 0 || searchPatterns . Length == 0 )
9285 {
@@ -108,6 +101,7 @@ public static async Task RunSharedInitialSyncAsync(
108101 }
109102
110103 var workQueue = new ConcurrentQueue < SyncWorkItem > ( ) ;
104+
111105 foreach ( var pair in EnumerateLocalDirectories ( localRootDirectory , remoteRootDirectory , excludedFolders ) )
112106 {
113107 foreach ( var pattern in searchPatterns )
@@ -117,6 +111,7 @@ public static async Task RunSharedInitialSyncAsync(
117111 }
118112
119113 var workers = new List < Task > ( ) ;
114+
120115 for ( int i = 0 ; i < workerCount ; i ++ )
121116 {
122117 workers . Add ( Task . Run ( async ( ) =>
@@ -150,7 +145,7 @@ public static async Task RunSharedInitialSyncAsync(
150145 /// <param name="destinationPath">Path to the destination file</param>
151146 public static void SyncFile ( SftpClient sftp , string sourcePath , string destinationPath )
152147 {
153- Logger . LogInfo ( $ "Syncing { sourcePath } -> { destinationPath } ") ;
148+ Logger . LogInfo ( $ "Syncing { sourcePath } ") ;
154149
155150 int retryCount = 0 ;
156151 const int maxRetries = 5 ;
@@ -195,7 +190,7 @@ public static Task<IEnumerable<FileInfo>> SyncDirectoryAsync(SftpClient sftp, st
195190 {
196191 if ( new DirectoryInfo ( sourcePath ) . EnumerateFiles ( searchPattern , SearchOption . TopDirectoryOnly ) . Any ( ) )
197192 {
198- Logger . LogInfo ( $ "Sync started for { sourcePath } \\ { searchPattern } -> { destinationPath } ") ;
193+ Logger . LogInfo ( $ "Sync started for { sourcePath } \\ { searchPattern } ") ;
199194
200195 return Task < IEnumerable < FileInfo > > . Factory . FromAsync ( sftp . BeginSynchronizeDirectories ,
201196 sftp . EndSynchronizeDirectories , sourcePath ,
@@ -214,7 +209,7 @@ private string[] FilteredDirectories(string localPath)
214209
215210 public async Task CreateDirectories ( string localPath , string remotePath )
216211 {
217- Logger . LogInfo ( $ "Creating directory { localPath } -> { remotePath } ") ;
212+ Logger . LogInfo ( $ "Creating directory { remotePath } ") ;
218213
219214 try
220215 {
@@ -337,6 +332,7 @@ private static async Task CreateDirectoriesInternal(
337332 var directoryName = item . Split ( Path . DirectorySeparatorChar ) . Last ( ) ;
338333 if ( ! remoteDirectories . ContainsKey ( directoryName ) )
339334 {
335+ Logger . LogInfo ( $ "Creating remote directory { remotePath } { directoryName } ") ;
340336 sftp . CreateDirectory ( remotePath + "/" + directoryName ) ;
341337 }
342338 await CreateDirectoriesInternal ( sftp , localRootDirectory , localPath + "\\ " + directoryName , remotePath + "/" + directoryName , excludedFolders ) ;
@@ -383,6 +379,19 @@ public static bool IsFileReady(String sFilename)
383379 }
384380 }
385381
382+ private string GetRemotePathForLocal ( string localPath )
383+ {
384+ var relativePath = Path . GetRelativePath ( _localRootDirectory , localPath ) ;
385+ if ( relativePath == "." || string . IsNullOrEmpty ( relativePath ) )
386+ return _remoteRootDirectory ;
387+
388+ relativePath = relativePath . Replace ( '\\ ' , '/' ) . TrimStart ( '/' ) ;
389+ if ( relativePath . Length == 0 )
390+ return _remoteRootDirectory ;
391+
392+ return _remoteRootDirectory + "/" + relativePath ;
393+ }
394+
386395 private void EnsureConnected ( )
387396 {
388397 if ( _disposed )
@@ -408,17 +417,25 @@ private bool EnsureConnectedSafe()
408417 }
409418 }
410419
420+ public Task < bool > ConnectAsync ( )
421+ {
422+ return Task . Run ( ( ) => EnsureConnectedSafe ( ) ) ;
423+ }
424+
411425
412426 private async void Fsw_Changed ( object ? sender , FileSystemEventArgs arg )
413427 {
414428 try
415429 {
430+ if ( ! DoneInitialSync . IsCompleted )
431+ return ;
432+
416433 if ( arg . ChangeType == WatcherChangeTypes . Changed || arg . ChangeType == WatcherChangeTypes . Created
417434 || arg . ChangeType == WatcherChangeTypes . Renamed )
418435 {
419436 var changedPath = Path . GetDirectoryName ( arg . FullPath ) ;
420- var relativePath = _localRootDirectory == changedPath ? "" : changedPath ? . Substring ( _localRootDirectory . Length ) . Replace ( ' \\ ' , '/' ) ;
421- var fullRemotePath = _remoteRootDirectory + relativePath ;
437+ var fullRemotePath = GetRemotePathForLocal ( changedPath ?? _localRootDirectory ) ;
438+ var fullRemoteFilePath = GetRemotePathForLocal ( arg . FullPath ) ;
422439 await Task . Yield ( ) ;
423440 bool makeDirectory = true ;
424441 lock ( _activeDirSync )
@@ -439,7 +456,7 @@ private async void Fsw_Changed(object? sender, FileSystemEventArgs arg)
439456 if ( connectionOk )
440457 {
441458 //check if we're a new directory
442- if ( makeDirectory && Directory . Exists ( arg . FullPath ) && ! _sftp . Exists ( fullRemotePath ) )
459+ if ( makeDirectory && changedPath != null && Directory . Exists ( changedPath ) && ! _sftp . Exists ( fullRemotePath ) )
443460 {
444461 _sftp . CreateDirectory ( fullRemotePath ) ;
445462 }
@@ -501,7 +518,7 @@ private async void Fsw_Changed(object? sender, FileSystemEventArgs arg)
501518 fileConnectionOk = EnsureConnectedSafe ( ) ;
502519 if ( fileConnectionOk )
503520 {
504- SyncFile ( _sftp , arg . FullPath , fullRemotePath + "/" + Path . GetFileName ( arg . FullPath ) ) ;
521+ SyncFile ( _sftp , arg . FullPath , fullRemoteFilePath ) ;
505522 }
506523 }
507524 finally
0 commit comments