I need to write a program in F# that should push files to an FTP server. Is there a library than I can use? I haven’t been able to find anything on the web. Can someone point me in t the right direction? If possible some sample code would be very helpful
System.Net.WebRequest.Create works well, when you give it a ftp:// URL.
To get access to FTP-specific functionality (such as uploading files), cast your WebRequest object to FtpWebRequest.
FtpWebRequest should do the thing.
The built-in FtpWebRequest is deprecated. FluentFTP is a great library that is being regularly updated.
#r "nuget: FluentFTP"
open FluentFTP
let ip = "45.33.2.79"
let username = "username"
let passwd = "s$cret"
let lpath = fsi.CommandLineArgs[1]
let rpath = fsi.CommandLineArgs[2]
let launch () =
use con = new FtpClient(ip, username, passwd)
con.Connect()
let status =
con.UploadFile(lpath, rpath, FtpRemoteExists.Overwrite, true, FtpVerify.Retry)
match status with
| FtpStatus.Success -> printfn "file uploaded OK"
| FtpStatus.Failed -> printfn "failed to upload file"
| _ -> printfn "skipped or unknown"
launch ()
The program uploads a local file to the FTP server.
Related
I have a SAFE stack app. I need to enable users to upload and download files.
Uploading works by making use of
Browser.Dom.FileReader.Create()
Is there a corresponding way to enable users to download files?
This answer offers a solution using a completely different mechanism which depends on a js library. Is there no mechanism that corresponds to the FileReader approach?
I have come up with the following which seems to work for me.
let downLoad fileName fileContent =
let anchor = Browser.Dom.document.createElement "a"
let encodedContent = fileContent |> sprintf "data:text/plain;charset=utf-8,%s" |> Fable.Core.JS.encodeURI
anchor.setAttribute("href", encodedContent)
anchor.setAttribute("download", fileName)
anchor.click()
In order to create a Json provider I need to pass a literal with the path. There are several people working on the project from different locations, and the paths are different in each case. (Actually only the beginning of each path). I tried to create a literal with pattern matching but the compiler does not accept it. Is there another way to do this?
My failed attempt is below:
open FSharp.Data
[<Literal>]
let bitbucketRoot = // Error message: This is not a valid constant expression
let computerName = Environment.MachineName
match computerName with
| "DESKTOP-G3OF32U" -> "C:\\Users\\Fernando"
| "HPW8" -> #"H:\Dropbox\"
| _ -> failwith "Unknown computer"
[<Literal>] // Error message: This is not a valid constant expression
let projDataPath = bitbucketRoot + #"Bitbucket\VSProjects\Fractal10\Fractal10\data\"
[<Literal>] // Error message: This is not a valid constant expression
let jsonPath = projDataPath + "fractal.json"
type PathInfo = JsonProvider<Sample=jsonPath>
I would advise that you store it in source control and make it a path relative to your project root, assuming you are working out of a common source control repository.
Either that, or host the sample on a public URL. (I wouldn't actually recommend this because including it in your source repository allows versioning and doesn't publicly expose your data)
You cannot create a conditional literal as the other comments point it out. However this is a fairly frequent use case and the way to deal with it is as follows:
#r #"..\packages\FSharp.Data\lib\net40\FSharp.Data.dll"
open FSharp.Data
open System
open System.IO
[<Literal>]
let JsonSource = __SOURCE_DIRECTORY__ + #"\test.json"
type JSonType = JsonProvider<JsonSource>
let json1 = JSonType.GetSamples()
let anotherPath = #"C:\tmp"
let anotherJson = anotherPath + #"\test.json"
let json2 = JSonType.Load(anotherJson)
The __SOURCE_DIRECTORY__ directive will point to the project root (just display it in the REPL) and then you can add the filename to it and make that a literal. If you check in this file into a git repo, then everyone who checks it out can have it in a relative path, and you can refer it when generating the type. When actually using the type or referring to the full file you can just use the .Load() method to load any file, and this doesn't have to be a literal.
There is actually a second way, which could work for you depending on the circumstances, compile a sample, and distribute it as a .dll. You can refer to this and use it directly without having access to the actual file. Please see the Using the JSON Provider in a Library section at the end of the documentation.
I have not tried referring to the json in a config file, it might also be possible.
I am trying to use F# to automate some Excel tasks. I actually got two issues:
1. If I open a workbook using an Excel.Application instance, I would miss all the add-ins that would have been automatically loaded if I had just opened the workbook in Excel. So I try to open the workbook in Excel first, with all add-ins loaded, and then hand the instance to F#. Then I got the second issue:
2. The Marshal.GetActiveObject("Excel.Application") actually open a new instance, instead of getting the existing instance. The code is below:
#r "Microsoft.Office.Interop.Excel"
#r "office"
open Microsoft.Office.Interop
open System
open System.Runtime.InteropServices
let app = Marshal.GetActiveObject("Excel.Application")
let app1 = app :?> Excel.Application
let wb = app1.ActiveWorkbook
let visible = app1.Visible
let n = app1.Workbooks.Count
and the output is:
val app : obj
val app1 : Excel.Application
val wb : Excel.Workbook = null
val visible : bool = false
val n : int = 0
But I am very sure I have an Excel instance running and it is visible. Thanks!
I have double checked my code and this is the way to use Excel from F# script (use Excel-DNA for other cases). However, very often (especially when using add-ins) Excel does not close properly and some semi-dead Excel processes hang in the task manager until they are manually killed. Probably you are getting a wrong process, not the one where you open your workbook.
The following code snippet is found in the FSharp.Data website http://fsharp.github.io/FSharp.Data/library/Http.html. The type of Text and Binary are string and byte[] respectively. It's not good to get the whole 2GB file in memory and then save it to a file.
let logoUrl = "https://raw.github.com/fsharp/FSharp.Data/master/misc/logo.png"
match Http.Request(logoUrl).Body with
| Text text ->
printfn "Got text content: %s" text
| Binary bytes ->
printfn "Got %d bytes of binary content" bytes.Length
I do not think you can keep the same code as on the FSharp.Data website to download huge files.
What I use to download large files is
async {
let! request = Http.AsyncRequestStream(logoUrl)
use outputFile = new System.IO.FileStream(fileName,System.IO.FileMode.Create)
do! request.ResponseStream.CopyToAsync( outputFile ) |> Async.AwaitTaskVoid
} |> Async.RunSynchronously
If you want to try to download infinite file check the complete source (run on your own risk, it is using the The Infinite File Download)
the following is a code sample that takes a list of file names and zips them into a single archive. The problem I'm having is that I'd like for the file described by filname be in the top level of the zip archive (i.e. when the archive is opened, "clientName....xml" is the first thing you see, instead of the folder "XML").
let filename = sprintf "C:\\XML\\ClientName_%s.xml" (System.DateTime.Now.ToString("ddMMyyyy"))
use fs = new FileStream(filename, FileMode.Create)
let xmlSerializer = XmlSerializer(typeof<log>)
xmlSerializer.Serialize(fs,logObj)
fs.Close()
use zipfile = new ZipFile()
let basePath = path.Replace("/", "\\")
for fileObj in files do
let relativeFilePath = basePath + (fileObj.Filename).Replace("/", "\\")
printfn "%s" relativeFilePath
zipfile.AddFile(relativeFilePath) |> ignore
()
zipfile.AddFile(filename) |> ignore
let zipFileName = sprintf "C:\\XML\\Compliance_%s.zip" (System.DateTime.Now.ToString("ddMMyyyy"))
zipfile.Save(zipFileName)
Where does the ZipFile type come from? I don't think this is a standard .NET class... I tried searching and found this library http://dotnetzip.codeplex.com/ which has a class matching to your sample :-)
The mentioned library also has AddFile overload that takes two string - the source file name and a relative file name in the ZIP file. This seems exactly like what you're looking for. I guess the call would be something like zipfile.AddFile(absolutePath, "/")...