Performing FTP transfers under APL program control using .NET
Contents
Retrieving a file using FTP
The first step is to create an FTP request as follows. Note that the FTP method is specified as Ftp.DownloadFile, and that the last line supplies the login name and password to access the FTP server
filename←'ftp://www.microapl.co.uk/docroot/apl/aplorder.pdf'
ftp←'.net' ⎕call 'System.Net.WebRequest.Create' filename
ftp.Method←'.net' ⎕call 'System.Net.WebRequestMethods+Ftp.DownloadFile'
ftp.Credentials←'.net' ⎕new 'System.Net.NetworkCredential' 'username' 'secretpassword'This will cause the APL session to log into the FTP server. We can check whether it worked:
response←ftp.GetResponse
response.BannerMessage
220 Demon Web Hosting & Homepages FTP Server
response.StatusCode
150
response.WelcomeMessage
230 User aplx logged in.Next we want to obtain the contents of the file downloaded and check it worked. (See below for an explanation of the 226 status code).
The following example is suitable for retrieving a text file - it will take care of issues like mapping line endings to the local machine's conventions, and handle any character-set conversion needed (e.g. EBCDIC to ASCII). The alternative is to transfer a file in Binary mode, an example of which is given at the end of this page.
reader←'.net' ⎕new 'System.IO.StreamReader' response.GetResponseStream
file←reader.ReadToEnd
response.StatusCode
226Finally we must close the FTP connection
reader.Close
response.Close
Sending a file using FTP
First open the connection, specifying the name of the file we wish to create on the FTP server. Note that the FTP method is specified as Ftp.UploadFile:
filename←'ftp://www.microapl.co.uk/docroot/apl/hello.txt'
ftp←'.net' ⎕call 'System.Net.WebRequest.Create' filename
ftp.Method←'.net' ⎕call 'System.Net.WebRequestMethods+Ftp.UploadFile'
ftp.Credentials←'.net' ⎕NEW 'System.Net.NetworkCredential' 'username' 'secretpassword'Next we need to upload the file contents. We could read this from a file on disk, but in this example we'll set the text to the string 'Hello World':
encoding←'.NET' ⎕NEW 'System.Text.ASCIIEncoding'
file←Contents←encoding.GetBytes.⎕REF 'Hello World'
stream←ftp.GetRequestStream
stream.Write file 0 file.Length
stream.CloseFinally we check that the transfer worked OK and close the FTP connection:
response←ftp.GetResponse
response.StatusCode
226
response.StatusDescription
226 Transfer complete.
response.Close
Listing a directory
The following example can be used to list the contents of a directory on the FTP server:
⍝ Directory to list
directory←'ftp://www.microapl.co.uk/docroot/apl'
⍝ Open connection
ftp←'.net' ⎕call 'System.Net.WebRequest.Create' directory
ftp.Method←'.net' ⎕call 'System.Net.WebRequestMethods+Ftp.ListDirectoryDetails'
ftp.Credentials←'.net' ⎕new 'System.Net.NetworkCredential' 'username' 'secretpassword'
⍝ Retrieve listing
response←ftp.GetResponse
reader←'.net' ⎕new 'System.IO.StreamReader' response.GetResponseStream
listing←reader.ReadToEnd
⍝ All finished
reader.Close
response.Close
FTP Status and Error Codes
Note that in FTP, status/error codes are three digit numbers with the first digit specifying the class of error :
1xx |
The requested action was initiated; expect another reply before proceeding with a new command. |
2xx |
The requested action has been successfully completed. |
3xx |
The command has been accepted, but the requested action is on hold, pending receipt of further information. |
4xx |
The command was not accepted and the requested action did not take place, but the error condition is temporary and the action may be requested again. |
5xx |
The command was not accepted and the requested action did not take place. |
Author: SimonMarsden
Dyalog APL Version
And, translated into Dyalog APL/W...
z←ftpget(filename username password);⎕IO;⎕ML;⎕USING;request;response;stream;reader;credentials
⍝ Get a file via ftp - from Dyalog documentation, and Marsden APLX example
⎕IO ⎕ML←0 3
⎕USING←'System,system.dll' 'System.Net' 'System.IO'
request←WebRequest.Create⊂filename
credentials←⎕NEW NetworkCredential(username password)
request.Credentials←credentials
response←request.GetResponse
⎕←'Banner Message : ',response.BannerMessage
⎕←'Status Code : ',response.StatusCode
⎕←'Welcome Message: ',response.WelcomeMessage
stream←response.GetResponseStream
reader←StreamReader.New stream
z←reader.ReadToEnd
reader.Close
stream.Close ftpsend(filename username password);⎕IO;⎕ML;⎕USING;request;response;stream;credentials;encoding;file
⍝ Send a file via ftp - from Dyalog documentation and Marsden APLX example
⎕IO ⎕ML←0 3
⎕USING←'System,system.dll' 'System.Net' 'System.IO' 'System.Text'
request←WebRequest.Create⊂filename
request.Method←'STOR'
credentials←⎕NEW NetworkCredential(username password)
request.Credentials←credentials
encoding←ASCIIEncoding.New''
file←encoding.GetBytes⊂'Hello World'
stream←request.GetRequestStream
stream.Write(file 0(⍬⍴⍴file))
stream.Close
response←request.GetResponse
⎕←'StatusCode : ',response.StatusCode
⎕←'StatusDescription: ',response.StatusDescription
response.Closez←ftpdirlist(dirname username password);⎕IO;⎕ML;⎕USING;request;credentials;response;reader;stream ⍝ Get a directory listing (from Dyalog documentation and Marsden APLX) ⎕IO ⎕ML←0 3 ⎕USING←'System,system.dll' 'System.Net' 'System.IO' request←WebRequest.Create⊂dirname request.Method←'LIST' credentials←⎕NEW NetworkCredential(username password) request.Credentials←credentials response←request.GetResponse stream←response.GetResponseStream reader←StreamReader.New stream z←reader.ReadToEnd reader.Close response.Close
Translator: DickBowman (e&oe)
The Binary Appendix
So - if you try the above to get a binary file, you'll (probably) find that it doesn't work - we have to explore a little more of the .Net quagmire, and arrive at...
z←ftpgetbin(filename username password);⎕IO;⎕ML;⎕USING;request;response;stream;reader;credentials;encoding
⍝ Get a binary file via ftp - from Dyalog documentation, and Marsden APLX example, and some frustrations
⎕IO ⎕ML←0 3
⎕USING←'System,system.dll' 'System.Net' 'System.IO' 'System.Text'
request←WebRequest.Create⊂filename
request.UseBinary←1
credentials←⎕NEW NetworkCredential(username password)
request.Credentials←credentials
response←request.GetResponse
stream←response.GetResponseStream
reader←BinaryReader.New stream
z←⍬
:Trap 90
:Repeat
z,←reader.ReadBytes 10000
:EndRepeat
:EndTrap
reader.Close
stream.Close
z←⎕UCS zWhich is a trifle clunky. Better minds than I will understand why StreamReader is able to grab the whole of a file in one mouthful, while BinaryReader takes dainty little nibbles (and adds insult to injury by making me resort to :Trap to figure out that I've got it all).
And a word of warning to anyone wanting to deploy this stuff for real - FTP servers are wayward gadgets, so you'll need to guard against such foibles as their tendencies to drop into a slumber as and when they feel like it.
Hacker: DickBowman
APL Wiki