Search code examples
c#ftpmainframeftpwebrequest

Trying to download from IBM mainframe using C#


I found a Windows command-line snippet to download a file from an IBM Z/OS mainframe that works and now I would like to replicate this code using C# and .NET. Here are the two Windows command-line files:

BatchFtp.bat:

rem 999.999.99.99 is mainframe IP Address
ftp -s:C:\scripts\script.txt 999.999.99.99
pause

C:\scripts\script.txt:

myid
MyPwd
cd ..
get "GIO.END.GPROD.PROC(MYDSNAME)" "c:\scripts\GIO.END.GPROD.PROC(MYDSNAME).txt"
quit

I've tried several C# FTP open source examples including the following but I get errors with all of them.

For example, when executing the following code, I get an error

The remote server returned an error: (425) Can't open data connection.

When executing this line:

ftpStream = ftpResponse.GetResponseStream();

Here's the code that I execute:

private void button1_Click(object sender, EventArgs e)
{
    /* Download a File */
    Ftp ftpClient = new Ftp("999.999.99.99", "MyUserIdHere", "MypasswordHere");

    ftpClient.download(@"GIO.END.GPROD.PROC(DsNmHere)", @"'c:\junk\GIO.END.GPROD.PROC(DsNmHere).txt'");
}

class Ftp
{
    private string host = null;
    private string user = null;
    private string pass = null;
    private FtpWebRequest ftpRequest = null;
    private FtpWebResponse ftpResponse = null;
    private Stream ftpStream = null;
    private int bufferSize = 2048;

    /* Construct Object */

    public Ftp(string hostIP, string userName, string password)
    {
        host = hostIP; 
        user = userName; 
        pass = password;
    }

    /* Download File */
    public void download(string remoteFile, string localFile)
    {
        try
        {
            /* Create an FTP Request */

            string fullPath = @"ftp://" + this.host + "/'" + remoteFile + "'";

            ftpRequest = (FtpWebRequest) FtpWebRequest.Create(fullPath);
            /* Log in to the FTP Server with the User Name and Password Provided */
            ftpRequest.Credentials = new NetworkCredential(user, pass);
            /* When in doubt, use these options */
            ftpRequest.UseBinary = false;
            ftpRequest.UsePassive = true;
            ftpRequest.KeepAlive = true;
            /* Specify the Type of FTP Request */
            ftpRequest.Method = WebRequestMethods.Ftp.DownloadFile;
            /* Establish Return Communication with the FTP Server */
            ftpResponse = (FtpWebResponse) ftpRequest.GetResponse();
            /* Get the FTP Server's Response Stream */
            ftpStream = ftpResponse.GetResponseStream();
            /* Open a File Stream to Write the Downloaded File */
            FileStream localFileStream = new FileStream(localFile, FileMode.Create);
            /* Buffer for the Downloaded Data */
            byte[] byteBuffer = new byte[bufferSize];
            int bytesRead = ftpStream.Read(byteBuffer, 0, bufferSize);
            /* Download the File by Writing the Buffered Data Until the Transfer is Complete */
            try
            {
                while (bytesRead > 0)
                {
                    localFileStream.Write(byteBuffer, 0, bytesRead);
                    bytesRead = ftpStream.Read(byteBuffer, 0, bufferSize);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            /* Resource Cleanup */
            localFileStream.Close();
            ftpStream.Close();
            ftpResponse.Close();
            ftpRequest = null;
        }
        catch (Exception ex)
        {
            System.Windows.Forms.MessageBox.Show(ex.Message);
            Console.WriteLine(ex.ToString());
        }
        return;
    }
}    

I am assuming that my URL must be constructed wrong. Once connected, how do I handle the cd (Change Directory) command found in the working batch file example?

I would appreciate any suggestions. Thanks.


Solution

  • Your ftp.exe scripts uses the active FTP mode. (The ftp.exe supports only the active mode.)

    While your C# code uses the passive FTP mode. And there's some problem with it, as the "Can't open data connection" message indicates.

    Try using the active mode by removing this line:

    ftpRequest.UsePassive = true;
    

    Though note that in general the passive mode tends to be less problematic. See my guide to understand why the passive mode prevails nowadays. But as you prove that the active mode works in your case, stick with that.


    To answer your other question: There's no way to issue the cd command with the FtpWebRequest. Just use a full path instead:

    ftp://host/../remotefile
    

    The ../ is there instead of the cd ..

    For a general information about working with paths with the FtpWebRequest against the IBM mainframe FTP server, see:
    Use C# to FTP file to mainframe including dataset - Translate FTP script to FtpWebRequest code