/// <summary> /// Transfers a file from the source FTP Server to the destination FTP Server via the FXP protocol asynchronously. /// </summary> private async Task <bool> TransferFileFXPInternalAsync(string sourcePath, FtpClient remoteClient, string remotePath, bool createRemoteDir, FtpRemoteExists existsMode, IProgress <FtpProgress> progress, CancellationToken token, FtpProgress metaProgress) { FtpReply reply; long offset = 0; bool fileExists = false; long fileSize = 0; var ftpFxpSession = await OpenPassiveFXPConnectionAsync(remoteClient, progress != null, token); if (ftpFxpSession != null) { try { ftpFxpSession.SourceServer.ReadTimeout = (int)TimeSpan.FromMinutes(30.0).TotalMilliseconds; ftpFxpSession.TargetServer.ReadTimeout = (int)TimeSpan.FromMinutes(30.0).TotalMilliseconds; // check if the file exists, and skip, overwrite or append if (existsMode == FtpRemoteExists.AppendNoCheck) { offset = await remoteClient.GetFileSizeAsync(remotePath, token); if (offset == -1) { offset = 0; // start from the beginning } } else { fileExists = await remoteClient.FileExistsAsync(remotePath, token); switch (existsMode) { case FtpRemoteExists.Skip: if (fileExists) { LogStatus(FtpTraceLevel.Info, "Skip is selected => Destination file exists => skipping"); //Fix #413 - progress callback isn't called if the file has already been uploaded to the server //send progress reports if (progress != null) { progress.Report(new FtpProgress(100.0, 0, 0, TimeSpan.FromSeconds(0), sourcePath, remotePath, metaProgress)); } return(true); } break; case FtpRemoteExists.Overwrite: if (fileExists) { await remoteClient.DeleteFileAsync(remotePath, token); } break; case FtpRemoteExists.Append: if (fileExists) { offset = await remoteClient.GetFileSizeAsync(remotePath, token); if (offset == -1) { offset = 0; // start from the beginning } } break; } } fileSize = await GetFileSizeAsync(sourcePath, token); // ensure the remote dir exists .. only if the file does not already exist! if (createRemoteDir && !fileExists) { var dirname = remotePath.GetFtpDirectoryName(); if (!await remoteClient.DirectoryExistsAsync(dirname, token)) { await remoteClient.CreateDirectoryAsync(dirname, token); } } if (offset == 0 && existsMode != FtpRemoteExists.AppendNoCheck) { // send command to tell the source server to 'send' the file to the destination server if (!(reply = await ftpFxpSession.SourceServer.ExecuteAsync($"RETR {sourcePath}", token)).Success) { throw new FtpCommandException(reply); } //Instruct destination server to store the file if (!(reply = await ftpFxpSession.TargetServer.ExecuteAsync($"STOR {remotePath}", token)).Success) { throw new FtpCommandException(reply); } } else { //tell source server to restart / resume if (!(reply = await ftpFxpSession.SourceServer.ExecuteAsync($"REST {offset}", token)).Success) { throw new FtpCommandException(reply); } // send command to tell the source server to 'send' the file to the destination server if (!(reply = await ftpFxpSession.SourceServer.ExecuteAsync($"RETR {sourcePath}", token)).Success) { throw new FtpCommandException(reply); } //Instruct destination server to append the file if (!(reply = await ftpFxpSession.TargetServer.ExecuteAsync($"APPE {remotePath}", token)).Success) { throw new FtpCommandException(reply); } } var transferStarted = DateTime.Now; long lastSize = 0; var sourceFXPTransferReply = ftpFxpSession.SourceServer.GetReplyAsync(token); var destinationFXPTransferReply = ftpFxpSession.TargetServer.GetReplyAsync(token); // while the transfer is not complete while (!sourceFXPTransferReply.IsCompleted || !destinationFXPTransferReply.IsCompleted) { // send progress reports every 1 second if (ftpFxpSession.ProgressServer != null) { // send progress reports if (progress != null && fileSize != -1) { offset = await ftpFxpSession.ProgressServer.GetFileSizeAsync(remotePath, token); if (offset != -1 && lastSize <= offset) { long bytesProcessed = offset - lastSize; lastSize = offset; ReportProgress(progress, fileSize, offset, bytesProcessed, DateTime.Now - transferStarted, sourcePath, remotePath, metaProgress); } } } await Task.Delay(FXPProgressInterval); } FtpTrace.WriteLine(FtpTraceLevel.Info, $"FXP transfer of file {sourcePath} has completed"); await NoopAsync(token); await remoteClient.NoopAsync(token); ftpFxpSession.Dispose(); return(true); } // Fix: catch all exceptions and dispose off the FTP clients if one occurs catch (Exception ex) { ftpFxpSession.Dispose(); throw ex; } } else { FtpTrace.WriteLine(FtpTraceLevel.Error, "Failed to open FXP passive Connection"); return(false); } }