Performing FTP transfers under APL program control using .NET

The following code shows how to transfer files using FTP, making use of the .NET System.Net.WebRequest class. The code is written for APLX, but can also be used with Dyalog after some modifications (see bottom of page).

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
226

Finally 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.Close

Finally 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.Close

z←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 z

Which 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


CategoryDotNet CategoryAplx

UsingDotNetFTPfromAPLX (last edited 2010-04-12 16:42:28 by SimonMarsden)