Skip to content

Instantly share code, notes, and snippets.

@giuliohome giuliohome/SFTPdownload.fs
Last active Mar 19, 2019

What would you like to do?
open Renci.SshNet
open System.IO
open System.Configuration
open System
let config = ConfigurationManager.AppSettings
let timestamp = "sftp_log_" + DateTime.UtcNow.Date.ToString("yyyy_MM_dd") + ".txt"
let logPath = Path.Combine(config.["SharedFolder"],timestamp)
let sw = new StreamWriter(logPath,true)
let printerAgent = MailboxProcessor.Start(fun inbox->
// the message processing function
let rec messageLoop() = async{
// read a message
let! msg = inbox.Receive()
// process a message
sw.WriteLine("{0}: {1}", DateTime.UtcNow.ToShortTimeString(), msg)
printfn "%s" msg
// loop to top
return! messageLoop()
// start the loop
type FtpNames = {Remote:string; Local:string }
type Node =
| FtpFile of FtpNames
| FtpDir of FtpNames
/// FSharp Async wrapper for SSH.NET SFTP
type SftpClient with
member x.ListDirectoryAsync path =
Async.FromBeginEnd((fun(iar,state) ->
x.BeginListDirectory(path, iar, state)), x.EndListDirectory)
member x.DownloadFileAsync path output =
Async.FromBeginEnd((fun(iar,state) ->
x.BeginDownloadFile(path, output, iar, state)), x.EndDownloadFile)
member x.UploadFileAsync input path =
Async.FromBeginEnd((fun(iar,state) ->
x.BeginUploadFile(input, path, iar, state)), x.EndUploadFile)
|> Async.Catch
member x.SynchronizeDirectoriesAsync sourcePath destinationPath searchPattern =
Async.FromBeginEnd((fun(iar,state) ->
sourcePath, destinationPath, searchPattern, iar, state)),
|> Async.Catch
let downloadFile (path:string) (client:SftpClient) (remote:string) =
async {
if not <| File.Exists path then
use fileStream = File.OpenWrite(path)
sprintf "Downloading %s..." path |> printerAgent.Post
do! client.DownloadFileAsync remote fileStream
sprintf "Downloaded %s (%i bytes)" path fileStream.Length |> printerAgent.Post
else printfn "File %s alreadfy exists" path
let rec downloadDir (local:string) (client:SftpClient) (workDir:string) =
async {
let! listDirectory = client.ListDirectoryAsync workDir
match listDirectory with
| empty when empty |> Seq.length = 0 -> sprintf "empty directory: %s" workDir |> printerAgent.Post
| _ ->
seq {
for file in listDirectory do
let path = local + file.FullName.Replace("/","\\")
if file.IsDirectory then
if not <| Directory.Exists(path) then
Directory.CreateDirectory(path) |> ignore
sprintf "Dir %s" path |> printerAgent.Post
yield downloadDir local client file.FullName
yield downloadFile path client file.FullName
|> Async.Parallel |> Async.Ignore
let sftpExample local host port username (password:string) =
async {
use client = new SftpClient(host, port, username, password)
sprintf "Connected to %s\nroot dir list" host |> printerAgent.Post
do! downloadDir local client ""
sprintf "Done, disconnecting now" |> printerAgent.Post
} |> Async.RunSynchronously
let main argv =
sftpExample config.["SharedFolder"] config.["SFTPFolder"] 22 "usr" "pswd" |> ignore
| ex ->
ex.Message |> printerAgent.Post
printfn "%s" ex.Message
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.