Xceed .NET Libraries Documentation
Performing a manual upload

Welcome to Xceed .NET, .NET Standard and Xamarin Libraries! > Task-Based Help > SFTP capabilities > Performing a manual upload

The upload mechanism in SFtp is provided by the SFtpFile.CopyTo method. The method

  1. Validates the supplied parameters by checking for null for example.
  2. Makes sure the upload makes sense by making sure the source exists, isn't the same as the destination, and other checks.
  3. Performs the upload by copying the data from the source to the destination.
  4. Applies the properties like dates, file attributes from the source file to the destination file.

In some scenarios, not all of these steps are desirable. For example, some SFtp servers remove files as soon as they are uploaded. This can cause problems with the last step of the upload procedure as the destination file might have disappeared before the properties can be applied.

In these cases, it might be best to avoid using the CopyTo method and perform uploads manually. This allows an application to focus on the data upload only and avoid operations the server cannot handle.

A manual upload is performed by opening streams for both the source and destination files and copying data read from the source stream to the destination stream in a loop until there is no more data to read. The amount of data read from the source in each iteration can be carefully chosen with the ComputeStreamBufferSize Method to generate the least overhead.

The following example shows how this can be done.

using System;
using System.IO;

using Xceed.SSH.Client;
using Xceed.SSH.Protocols;
using Xceed.SSH.Core;
using Xceed.FileSystem;

namespace DocumentationExamples.SSH
{
  public class ManualCopyTo
  {
    public static void Example()
    {
      string host = "sftptest.dreamhosters.com";
      string username = "snippet_sftp";
      string password = "9MNfGgSx";

      using( SSHClient ssh = new SSHClient() )
      {
        ssh.Connect( host );
        ssh.Authenticate( username, password );

        // Start a SFtp session from the SSH client
        using( SFtpSession sftp = new SFtpSession( ssh ) )
        {
          // Select a local file
          AbstractFile sourceFile = new DiskFile( @"SomeFile.dat" );

          // Select a remote destination folder
          AbstractFolder destinationFolder = new SFtpFolder( sftp, "SomeFolder" );

          // Select the destination file in the destination folder using the name of the source file
          AbstractFile destinationFile = destinationFolder.GetFile( sourceFile.Name );

          if( !sourceFile.Exists )
            throw new InvalidOperationException( "Source file does not exist." );

          if( sourceFile.IsSameAs( destinationFile ) )
            throw new InvalidOperationException( "Cannot copy a file unto itself." );

          // Open the source file for reading
          using( Stream sourceStream = sourceFile.OpenRead() )
          {
            Stream destinationStream = null;

            try
            {
              // If the destination file already exists
              if( destinationFile.Exists )
              {
                // Open it for writing, overwriting its previous contents
                destinationStream = destinationFile.OpenWrite( true );
              }
              else
              {
                // Create it and open it for writing in one operation
                destinationStream = destinationFile.CreateWrite();
              }

              /* SSH has an overhead and several rules that define a maximum logical packet
               size.
               
               We are free to use any reasonable buffer size we want and it will work.
               However, using a value too small will generate more overhead than needed.
               Using a value too large will force the component to split the data into
               several packets and increase overhead as well.
                
               SFtpSession has a method that computes the optimal buffer size for the
               SFtp stream. It takes into account the effective overhead packets will have
               and computes a value that will make it so that every call to Stream.Write()
               will end up creating the biggest SSH packet allowed by the server that does
               not generate extra overhead.
                
               This provides the best throughput in a file transfer. */
              int bufferSize = sftp.ComputeStreamBufferSize( destinationStream );

              // Create a buffer for the streams
              byte[] buffer = new byte[ bufferSize ];
              int read;

              // While we have data to read from the source stream
              while( ( read = sourceStream.Read( buffer, 0, bufferSize ) ) > 0 )
              {
                // Write the data to the destination stream
                destinationStream.Write( buffer, 0, read );
              }
            }
            finally
            {
              // If we have a destination stream
                if( destinationStream != null )
              {
                // Close it
                destinationStream.Close();
              }
            }
          }
        }
      }
    }
  }
}
Imports Microsoft.VisualBasic
Imports System
Imports System.IO

Imports Xceed.SSH.Client
Imports Xceed.SSH.Protocols
Imports Xceed.SSH.Core
Imports Xceed.FileSystem

Namespace DocumentationExamples.SSH
  Public Class ManualCopyTo
    Public Shared Sub Example()
      Dim host As String = "sftptest.dreamhosters.com"
      Dim username As String = "snippet_sftp"
      Dim password As String = "9MNfGgSx"

      Using ssh As New SSHClient()
        ssh.Connect(host)
        ssh.Authenticate(username, password)

        ' Start a SFtp session from the SSH client
        Using sftp As New SFtpSession(ssh)
          ' Select a local file
          Dim sourceFile As AbstractFile = New DiskFile("SomeFile.dat")

          ' Select a remote destination folder
          Dim destinationFolder As AbstractFolder = New SFtpFolder(sftp, "SomeFolder")

          ' Select the destination file in the destination folder using the name of the source file
          Dim destinationFile As AbstractFile = destinationFolder.GetFile(sourceFile.Name)

          If (Not sourceFile.Exists) Then
            Throw New InvalidOperationException("Source file does not exist.")
          End If

          If sourceFile.IsSameAs(destinationFile) Then
            Throw New InvalidOperationException("Cannot copy a file unto itself.")
          End If

          ' Open the source file for reading
          Using sourceStream As Stream = sourceFile.OpenRead()
            Dim destinationStream As Stream = Nothing

            Try
              ' If the destination file already exists
              If destinationFile.Exists Then
                ' Open it for writing, overwriting its previous contents
                destinationStream = destinationFile.OpenWrite(True)
              Else
                ' Create it and open it for writing in one operation
                destinationStream = destinationFile.CreateWrite()
              End If

'               SSH has an overhead and several rules that define a maximum logical packet
'               size.
'               
'               We are free to use any reasonable buffer size we want and it will work.
'               However, using a value too small will generate more overhead than needed.
'               Using a value too large will force the component to split the data into
'               several packets and increase overhead as well.
'                
'               SFtpSession has a method that computes the optimal buffer size for the
'               SFtp stream. It takes into account the effective overhead packets will have
'               and computes a value that will make it so that every call to Stream.Write()
'               will end up creating the biggest SSH packet allowed by the server that does
'               not generate extra overhead.
'                
'               This provides the best throughput in a file transfer. 
              Dim bufferSize As Integer = sftp.ComputeStreamBufferSize(destinationStream)

              ' Create a buffer for the streams
              Dim buffer(bufferSize - 1) As Byte
              Dim read As Integer

              ' While we have data to read from the source stream
              read = sourceStream.Read(buffer, 0, bufferSize)
              Do While read > 0
                ' Write the data to the destination stream
                destinationStream.Write(buffer, 0, read)
                read = sourceStream.Read(buffer, 0, bufferSize)
              Loop
            Finally
              ' If we have a destination stream
              If destinationStream IsNot Nothing Then
                ' Close it
                destinationStream.Close()
              End If
            End Try
          End Using
        End Using
      End Using
    End Sub
  End Class
End Namespace