Exemplo n.º 1
0
        /// <summary>
        /// Sends progress to the user, either a value between 0-100 indicating percentage complete, or -1 for indeterminate.
        /// </summary>
        private void ReportProgress(IProgress <FtpProgress> progress, long fileSize, long position, long bytesProcessed, TimeSpan elapsedtime, string localPath, string remotePath, FtpProgress metaProgress)
        {
            //  calculate % done, transfer speed and time remaining
            FtpProgress status = FtpProgress.Generate(fileSize, position, bytesProcessed, elapsedtime, localPath, remotePath, metaProgress);

            // send progress to parent
            progress.Report(status);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Create a new FtpProgress object for individual file transfer progress.
        /// </summary>
        public FtpProgress(double progress, long bytesTransferred, double transferspeed, TimeSpan remainingtime, string localPath, string remotePath, FtpProgress metaProgress)
        {
            // progress of individual file transfer
            Progress         = progress;
            TransferSpeed    = transferspeed;
            ETA              = remainingtime;
            LocalPath        = localPath;
            RemotePath       = remotePath;
            TransferredBytes = bytesTransferred;

            // progress of the entire task
            if (metaProgress != null)
            {
                FileCount = metaProgress.FileCount;
                FileIndex = metaProgress.FileIndex;
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Create a new FtpProgress object for a file transfer and calculate the ETA, Percentage and Transfer Speed.
        /// </summary>
        public static FtpProgress Generate(long fileSize, long position, long bytesProcessed, TimeSpan elapsedtime, string localPath, string remotePath, FtpProgress metaProgress)
        {
            // default values to send
            double progressValue        = -1;
            double transferSpeed        = 0;
            var    estimatedRemaingTime = TimeSpan.Zero;

            // catch any divide-by-zero errors
            try {
                // calculate raw transferSpeed (bytes per second)
                transferSpeed = bytesProcessed / elapsedtime.TotalSeconds;

                // If fileSize < 0 the below computations make no sense
                if (fileSize > 0)
                {
                    // calculate % based on file length vs file offset
                    // send a value between 0-100 indicating percentage complete
                    progressValue = (double)position / (double)fileSize * 100;

                    //calculate remaining time
                    estimatedRemaingTime = TimeSpan.FromSeconds((fileSize - position) / transferSpeed);
                }
            }
            catch (Exception) {
            }

            // suppress invalid values and send -1 instead
            if (double.IsNaN(progressValue) && double.IsInfinity(progressValue))
            {
                progressValue = -1;
            }
            if (double.IsNaN(transferSpeed) && double.IsInfinity(transferSpeed))
            {
                transferSpeed = 0;
            }

            var p = new FtpProgress(progressValue, position, transferSpeed, estimatedRemaingTime, localPath, remotePath, metaProgress);

            return(p);
        }
        /// <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);
            }
        }
        /// <summary>
        /// Transfer the specified file from the source FTP Server to the destination FTP Server using the FXP protocol.
        /// High-level API that takes care of various edge cases internally.
        /// </summary>
        /// <param name="sourcePath">The full or relative path to the file on the source FTP Server</param>
        /// <param name="remoteClient">Valid FTP connection to the destination FTP Server</param>
        /// <param name="remotePath">The full or relative path to destination file on the remote FTP Server</param>
        /// <param name="createRemoteDir">Indicates if the folder should be created on the remote FTP Server</param>
        /// <param name="existsMode">If the file exists on disk, should we skip it, resume the download or restart the download?</param>
        /// <param name="verifyOptions">Sets if checksum verification is required for a successful download and what to do if it fails verification (See Remarks)</param>
        /// <param name="progress">Provide a callback to track download progress.</param>
        /// Returns a FtpStatus indicating if the file was transfered.
        /// <remarks>
        /// If verification is enabled (All options other than <see cref="FtpVerify.None"/>) the hash will be checked against the server.  If the server does not support
        /// any hash algorithm, then verification is ignored.  If only <see cref="FtpVerify.OnlyChecksum"/> is set then the return of this method depends on both a successful
        /// upload &amp; verification.  Additionally, if any verify option is set and a retry is attempted then overwrite will automatically be set to true for subsequent attempts.
        /// </remarks>
        public FtpStatus TransferFile(string sourcePath, FtpClient remoteClient, string remotePath,
                                      bool createRemoteDir = false, FtpRemoteExists existsMode = FtpRemoteExists.Append, FtpVerify verifyOptions = FtpVerify.None, Action <FtpProgress> progress = null, FtpProgress metaProgress = null)
        {
            LogFunc(nameof(TransferFile), new object[] { sourcePath, remoteClient, remotePath, FXPDataType, createRemoteDir, existsMode, verifyOptions });

            // verify input params
            VerifyTransferFileParams(sourcePath, remoteClient, remotePath);

            // ensure source file exists
            if (!FileExists(sourcePath))
            {
                throw new FtpException("Source File " + sourcePath + " cannot be found or does not exists!");
            }

            bool fxpSuccess;
            var  verified     = true;
            var  attemptsLeft = verifyOptions.HasFlag(FtpVerify.Retry) ? m_retryAttempts : 1;

            do
            {
                fxpSuccess = TransferFileFXPInternal(sourcePath, remoteClient, remotePath, createRemoteDir, existsMode, progress, metaProgress is null ? new FtpProgress(1, 0) : metaProgress);
                attemptsLeft--;

                // if verification is needed
                if (fxpSuccess && verifyOptions != FtpVerify.None)
                {
                    verified = VerifyFXPTransfer(sourcePath, remoteClient, remotePath);
                    LogStatus(FtpTraceLevel.Info, "File Verification: " + (verified ? "PASS" : "FAIL"));
                    if (!verified && attemptsLeft > 0)
                    {
                        LogStatus(FtpTraceLevel.Verbose, "Retrying due to failed verification." + (existsMode == FtpRemoteExists.Append ? "  Overwrite will occur." : "") + "  " + attemptsLeft + " attempts remaining");
                        // Force overwrite if a retry is required
                        existsMode = FtpRemoteExists.Overwrite;
                    }
                }
            } while (!verified && attemptsLeft > 0);

            if (fxpSuccess && !verified && verifyOptions.HasFlag(FtpVerify.Delete))
            {
                remoteClient.DeleteFile(remotePath);
            }

            if (fxpSuccess && !verified && verifyOptions.HasFlag(FtpVerify.Throw))
            {
                throw new FtpException("Destination file checksum value does not match source file");
            }

            return(fxpSuccess && verified ? FtpStatus.Success : FtpStatus.Failed);
        }
Exemplo n.º 6
0
        /// <summary>
        /// Transfer the specified file from the source FTP Server to the destination FTP Server asynchronously using the FXP protocol.
        /// High-level API that takes care of various edge cases internally.
        /// </summary>
        /// <param name="sourcePath">The full or relative path to the file on the source FTP Server</param>
        /// <param name="remoteClient">Valid FTP connection to the destination FTP Server</param>
        /// <param name="remotePath">The full or relative path to destination file on the remote FTP Server</param>
        /// <param name="createRemoteDir">Indicates if the folder should be created on the remote FTP Server</param>
        /// <param name="existsMode">If the file exists on disk, should we skip it, resume the download or restart the download?</param>
        /// <param name="verifyOptions">Sets if checksum verification is required for a successful download and what to do if it fails verification (See Remarks)</param>
        /// <param name="progress">Provide a callback to track download progress.</param>
        /// <param name="token">The token that can be used to cancel the entire process</param>
        /// Returns a FtpStatus indicating if the file was transfered.
        /// <remarks>
        /// If verification is enabled (All options other than <see cref="FtpVerify.None"/>) the hash will be checked against the server.  If the server does not support
        /// any hash algorithm, then verification is ignored.  If only <see cref="FtpVerify.OnlyChecksum"/> is set then the return of this method depends on both a successful
        /// upload &amp; verification.  Additionally, if any verify option is set and a retry is attempted then overwrite will automatically be set to true for subsequent attempts.
        /// </remarks>
        public async Task <FtpStatus> TransferFileAsync(string sourcePath, FtpClient remoteClient, string remotePath,
                                                        bool createRemoteDir = false, FtpRemoteExists existsMode = FtpRemoteExists.Append, FtpVerify verifyOptions = FtpVerify.None, IProgress <FtpProgress> progress = null, FtpProgress metaProgress = null, CancellationToken token = default(CancellationToken))
        {
            LogFunc(nameof(TransferFileAsync), new object[] { sourcePath, remoteClient, remotePath, FXPDataType, createRemoteDir, existsMode, verifyOptions });

            // verify input params
            VerifyTransferFileParams(sourcePath, remoteClient, remotePath);

            // ensure source file exists
            if (!await FileExistsAsync(sourcePath, token))
            {
                throw new FtpException(string.Format("Source File {0} cannot be found or does not exists!", sourcePath));
            }

            bool fxpSuccess;
            var  verified     = true;
            var  attemptsLeft = verifyOptions.HasFlag(FtpVerify.Retry) ? m_retryAttempts : 1;

            do
            {
                fxpSuccess = await TransferFileFXPInternalAsync(sourcePath, remoteClient, remotePath, createRemoteDir, existsMode, progress, token, metaProgress is null?new FtpProgress(1, 0) : metaProgress);

                attemptsLeft--;

                // if verification is needed
                if (fxpSuccess && verifyOptions != FtpVerify.None)
                {
                    verified = await VerifyFXPTransferAsync(sourcePath, remoteClient, remotePath, token);

                    LogStatus(FtpTraceLevel.Info, "File Verification: " + (verified ? "PASS" : "FAIL"));
#if DEBUG
                    if (!verified && attemptsLeft > 0)
                    {
                        LogStatus(FtpTraceLevel.Verbose, "Retrying due to failed verification." + (existsMode == FtpRemoteExists.Append ? "  Overwrite will occur." : "") + "  " + attemptsLeft + " attempts remaining");
                    }
#endif
                }
            } while (!verified && attemptsLeft > 0);

            if (fxpSuccess && !verified && verifyOptions.HasFlag(FtpVerify.Delete))
            {
                await remoteClient.DeleteFileAsync(remotePath, token);
            }

            if (fxpSuccess && !verified && verifyOptions.HasFlag(FtpVerify.Throw))
            {
                throw new FtpException("Destination file checksum value does not match source file");
            }

            return(fxpSuccess && verified ? FtpStatus.Success : FtpStatus.Failed);
        }