Skip to content

SFTP Examples

Andrew Lambert edited this page Nov 26, 2022 · 16 revisions

SSH File Transfer Protocol

SFTP is a file transfer protocol designed as an extension of the SSH2 protocol. Despite similar names and features, SFTP is not related to the File Transfer Protocol (FTP/FTPS). If you need to tunnel a FTP session through SSH then refer to the section on TCP tunneling.

Performance considerations

The specification of the SFTP protocol was never finished, and there are some questionable design choices in the drafts that are most commonly used. The most important of these are the maximum packet size (32KB) and the requirement that each SFTP packet be individually acknowledged before the next one is sent. Combined, these factors can create a bottleneck that slows down SFTP transfers, particularly on connections with high latency. Recent versions of libssh2 try to mitigate this by dispatching several packets at a time without waiting for the acknowledgement, so to achieve maximum throughput you should try to read/write at least (and preferably an exact multiple of) 32KB at a time.

Download

This example downloads a file over SFTP. For extended features like reading file metadata refer to the SFTPStream class:

  Dim session As SSH.Session = SSH.Connect("ssh://user:password@public.example.com/")
  Dim sftp As New SSH.SFTPSession(session)
  Dim reader As SSHStream = sftp.Get("file.txt")
  Dim writer As BinaryStream = BinaryStream.Create(SpecialFolder.Desktop.Child("file.txt"))
  
  Do Until reader.EOF()
    writer.Write(reader.Read(1024 * 1024 * 32))
  Loop
  
  reader.Close()
  writer.Close()

Upload

This example uploads a file over SFTP. For extended features like writing file metadata refer to the SFTPStream class:

  Dim session As SSH.Session = SSH.Connect("ssh://user:password@public.example.com/")
  Dim sftp As New SSH.SFTPSession(session)
  Dim writer As SSHStream = sftp.Put("file.txt")
  Dim reader As BinaryStream = BinaryStream.Open(SpecialFolder.Desktop.Child("file.txt"))
  
  Do Until reader.EOF()
    writer.Write(reader.Read(1024 * 1024 * 32))
  Loop
  
  reader.Close()
  writer.Close()

Read file permissions

This example reads the Unix-style permissions of a file on the server:

  Dim session As SSH.Session = SSH.Connect("ssh://user:password@public.example.com/")
  Dim sftp As New SSH.SFTPSession(session)
  If sftp.PathExists("/path/to/the/file.txt") Then
    Dim stream As SSH.SFTPStream = sftp.Get("/path/to/the/file.txt")
    Dim perms As Permissions = stream.Mode
  End If

Write file permissions

This example updates the Unix-style permissions of a file on the server:

  Dim session As SSH.Session = SSH.Connect("ssh://user:password@public.example.com/")
  Dim sftp As New SSH.SFTPSession(session)
  If sftp.PathExists("/path/to/the/file.txt") Then
    Dim stream As SSH.SFTPStream = sftp.Append("/path/to/the/file.txt")
    stream.Mode = New Permissions(&o744)
  End If

List directory

This example uses the SFTPDirectory class to get the names of files/subdirectories in a remote directory using SFTP:

  Dim session As SSH.Session = SSH.Connect("ssh://user:password@public.example.com/")
  Dim sftp As New SSH.SFTPSession(session)
  Dim names() As String
  Dim lister As SSH.SFTPDirectory = sftp.ListDirectory("/path/to/dir/")
  
  Do
    names.Append(lister.CurrentName)
  Loop Until Not lister.ReadNextEntry()

  lister.Close()

Recursive download

This example uses the SFTPTransferQueue class to manage simultaneous downloads while recursively downloading a remote directory tree.

Sub DownloadDirectory(Session As SSH.SFTPSession, Queue As SSH.SFTPTransferQueue, DirectoryPath As String, LocalDirectory As FolderItem, Optional RelativeRoot As String)
  ' normalize the DirectoryPath
  If Right(DirectoryPath, 1) <> "/" Then DirectoryPath = DirectoryPath + "/"
  If RelativeRoot = "" Then RelativeRoot = DirectoryPath
  Dim relativepath As String = Replace(DirectoryPath, RelativeRoot, "")
  
  ' locate or create the corresponding local directory
  Dim thisdir As FolderItem = LocalDirectory
  Dim path() As String = relativepath.Split("/")
  For i As Integer = 0 To UBound(path)
    If path(i).Trim = "" Then Continue
    thisdir = thisdir.Child(path(i))
    If thisdir.Directory Then Continue
    If thisdir.Exists Then Raise New IOException ' file exists with the name of a directory we need
    thisdir.CreateAsFolder()
  Next
  
  Dim startcount As Integer = Queue.Count
  ' begin listing the contents of DirectoryPath
  Dim lister As SSH.SFTPDirectory = Session.ListDirectory(DirectoryPath)
  Do
    Select Case lister.CurrentType
    Case SSH.SFTPEntryType.Unknown
      ' skip. Either a weird custom type or the directory is empty
      Continue
      
    Case SSH.SFTPEntryType.Directory
      ' recurse into the subdirectory
      DownloadDirectory(Session, Queue, DirectoryPath + lister.CurrentName, LocalDirectory, RelativeRoot)
      
    Else
      ' prepare the download
      Dim reader As SSH.SFTPStream = lister.OpenFile()
      Dim file As FolderItem = thisdir.Child(lister.CurrentName)
      Dim writer As BinaryStream = BinaryStream.Create(file)
      
      ' run the queue for a bit if needed
      Do Until Queue.Count < Queue.MaxCount
        If Not Queue.PerformOnce() Then Exit Do
      Loop
      
      ' add the download to the queue
      Queue.AddDownload(reader, writer)

    End Select
    
  Loop Until Not lister.ReadNextEntry()
  lister.Close()
  
  ' run the queue until there are fewer downloads remaining than we started with
  Do Until Queue.Count <= startcount
    If Not Queue.PerformOnce() Then Exit Do
  Loop
End Sub

Usage example:

  Dim session As SSH.Session = SSH.Connect("ssh://user:password@public.example.com/")
  Dim sftp As New SSH.SFTPSession(session)
  Dim queue As New SSH.SFTPTransferQueue
  Dim localroot As FolderItem = SelectFolder()
  DownloadDirectory(sftp, queue, "/home/username/example/", localroot)

Recursive upload

This example uses the SFTPTransferQueue class to manage simultaneous uploads while recursively uploading a local directory tree.

Sub UploadDirectory(Session As SSH.SFTPSession, Queue As SSH.SFTPTransferQueue, DirectoryPath As String, LocalDirectory As FolderItem)
  ' Normalize the DirectoryPath and create the remote directory if needed
  If Right(DirectoryPath, 1) <> "/" Then DirectoryPath = DirectoryPath + "/"
  If Not Session.PathExists(DirectoryPath) Then
    Session.MakeDirectory(DirectoryPath)
  End If
  
  Dim startcount As Integer = Queue.Count
  
  ' begin listing the contents of LocalDirectory
  Dim c As Integer = LocalDirectory.Count
  For i As Integer = 1 To c
    Dim item As FolderItem = LocalDirectory.Item(i)
    If item.Directory Then
      ' recurse into the subdirectory
      UploadDirectory(Session, Queue, DirectoryPath + item.Name, item)
      
    Else
      ' prepare the upload
      Dim reader As BinaryStream = BinaryStream.Open(item)
      Dim writer As SSH.SFTPStream = Session.Put(DirectoryPath + item.Name)
      
      ' run the queue for a bit if needed
      Do Until Queue.Count < Queue.MaxCount
        If Not Queue.PerformOnce() Then Exit Do
      Loop
      
      ' add the upload to the queue
      Queue.AddUpload(writer, reader)

    End If
  Next
  
  ' run the queue until there are fewer uploads remaining than we started with
  Do Until Queue.Count <= startcount
    If Not Queue.PerformOnce() Then Exit Do
  Loop
End Sub

Usage example:

  Dim session As SSH.Session = SSH.Connect("ssh://user:password@public.example.com/")
  Dim sftp As New SSH.SFTPSession(session)
  Dim queue As New SSH.SFTPTransferQueue
  Dim localroot As FolderItem = SelectFolder()
  UploadDirectory(sftp, queue, "/home/user/example/", localroot)
Clone this wiki locally