Link to home
Start Free TrialLog in
Avatar of tom_corc3
tom_corc3

asked on

Basic FTP using Java

I need to write a Java application to upload a textfile (no extension) from my hard disk onto an ftp site (with an ID and password). The fle must be placed on a particular directory on the site .The program also has check another directory on the same site for specific files.
Can this be done easily in Java? Does anybody have the necessary source code?
Avatar of Michel Plungjan
Michel Plungjan
Flag of Denmark image

Yes it is very possible. You can get several ftp clients.
I am looking for one for you

Michel
Avatar of manhthang
manhthang

Here is some other source code that I have handy. Hope it helps.

//---------FTPSession.java
import java.io.*;
import java.net.*;
import java.util.*;

// This class implements file storage and retrieval using the
// FTP protocol. Your server must support the PASV command
// for this to work.
//

public class FTPSession extends Object
{
      public String host;      // Host name we connect to
      public int port;      // port number we connect to, default=21
      public String username;      // Username to log in with
      public String password;      // Password to log in with
      public String acct;      // optional acct for logging in

      protected Socket sessionSock;      // The control socket

      protected DataInputStream inStream;
      protected DataOutputStream outStream;

      public FTPSession()
      {
      }

// This should be the most common constructor, it opens up an FTP session
// with the named host using the default FTP port and logs on using
// the username and password.

      public FTPSession(String host, String username, String password)
      throws IOException
      {
            this.host = host;
            this.port = 21;      // default FTP port is 21

            this.username = username;
            this.password = password;

            logon();      // go ahead and log on to the server
      }

// Opens up an FTP session with the named host using the default FTP
// port and logs on using the username and password, and account.
// The account field is pretty rare on FTP servers.

      public FTPSession(String host, String username, String password,
             String acct)
      throws IOException
      {
            this.host = host;
            this.port = 21;      // default FTP port is 21

            this.username = username;
            this.password = password;
            this.acct = acct;

            logon();      // go ahead and log on to the server
      }

// Opens up an FTP session with the named host using an alternate
// port number and if logOn is true, logs on using the username and password.

      public FTPSession(String host, int port, String username,
            String password, boolean logOn)
      throws IOException
      {
            this.host = host;
            this.port = port;
            if (this.port <= 0) this.port = 21;

            this.username = username;
            this.password = password;

            if (logOn) {
                  logon();
            }
      }

// Opens up an FTP session with the named host using an alternate
// port number and if logOn is true, logs on using the username,
// password and account.

      public FTPSession(String host, int port, String username,
            String password, String acct, boolean logOn)
      throws IOException
      {
            this.host = host;
            this.port = port;
            if (this.port <= 0) this.port = 21;

            this.username = username;
            this.password = password;
            this.acct = acct;

            if (logOn) {
                  logon();
            }
      }

// Close down the session

      public void close()
      throws IOException
      {
            sessionSock.close();
            sessionSock = null;
      }

// Connect to the server

      public void connect()
      throws IOException
      {
            sessionSock = new Socket(host, port);
            inStream = new DataInputStream(
                  sessionSock.getInputStream());
            outStream = new DataOutputStream(
                  sessionSock.getOutputStream());

      }

// Send a command and wait for a response

      public String doCommand(String commandString)
      throws IOException
      {
            outStream.writeBytes(commandString+"\n");
            String response = getResponse();
            return response;
      }

// Get a response back from the server. Handles multi-line responses
// and returns them as part of the string.

      public String getResponse()
      throws IOException
      {
            String response = "";

            for (;;) {
                  String line = inStream.readLine();

                  if (line == null) {
                        throw new IOException(
                              "Bad response from server.");
                  }

// FTP response lines should at the very least have a 3-digit number

                  if (line.length() < 3) {
                        throw new IOException(
                              "Bad response from server.");
                  }
                  response += line + "\n";

// If there isn't a '-' immediately after the number, we've gotten the
// complete response. ('-' is the continuation character for FTP responses)

                  if ((line.length() == 3) ||
                        (line.charAt(3) != '-')) return response;
            }
      }


// Logs on to the FTP server

      public void logon()
      throws IOException
      {
            connect();

// After connecting, the FTP server will send a response string. Make
// sure it starts with a '2' (reponses in the 200's are positive
// responses.

            String response = getResponse();
            if (response.charAt(0) != '2') {
                  throw new IOException(response);
            }

// Send a logon command
            response = doCommand("USER "+username);

// If we get a response in the 300's, send a password

            if (response.charAt(0) == '3') {
                  response = doCommand("PASS "+password);

// If we get a response in the 300's on the password command,
// send the account
                  if (response.charAt(0) == '3') {
                        response = doCommand("ACCT "+acct);
                  }
            }

// If the last response we got wasn't in the 200's, there was an error
// during the logon.

            if (response.charAt(0) != '2') {
                  throw new IOException(response);
            }
      }

// Creates a data connection to the server by using the PASV command.
// Normally the data connection is set up by the client sending the
// server an address and port number using the PORT command. Unfortunately,
// since an applet cannot listen for incoming connections, we can't work
// that way. The PASV command asks the server to accept a connection.
// The response for the PASV command contains a host address and port
// number in the form h,h,h,h,p,p where h is a byte in the host address
// and p is a byte in the port.

      protected synchronized Socket doPasvPort()
      throws IOException
      {

// Send the PASV command
            String response = doCommand("PASV");

// If it wasn't in the 200's, there was an error

            if (response.charAt(0) != '2') {
                  throw new IOException(response);
            }

// The pasv response looks like:
// 227 Entering Passive Mode (127,0,0,1,4,160)
// We'll look for the ()'s at the end first

            int parenStart = response.lastIndexOf('(');
            int parenEnd = response.lastIndexOf(')');

// Make sure they're both there and that the ) comes after the (
            if ((parenStart < 0) || (parenEnd < 0) ||
                  (parenStart >= parenEnd)) {
                  throw new IOException("PASV response format error");
            }

// Extract the address bytes
            String pasvAddr = response.substring(parenStart+1, parenEnd);

// Create a tokenizer to parse the bytes
            StringTokenizer tokenizer = new StringTokenizer(pasvAddr, ",");

// Create the array to store the bytes
            int[] addrValues = new int[6];

// Parse each byte
            for (int i=0; (i < 6) && tokenizer.hasMoreTokens(); i++) {
                  try {
                        addrValues[i] = Integer.valueOf(
                              tokenizer.nextToken()).intValue();
                  } catch (Exception e) {
                        throw new IOException(
                              "PASV response format error");
                  }
            }

// We ignore the host addresses, assuming that the host address is
// the same as the host address we used to connect the first time.

            Socket newSock = new Socket(host, (addrValues[4] << 8) +
                  addrValues[5]);

            return newSock;
      }

// Fetches a file in binary mode and returns it as an array of bytes

      public byte[] get(String remoteFile)
      throws IOException
      {
            return get(remoteFile, true);      // default to binary
      }

// Fetches a file in binary mode if doBinary is true, or ascii mode if
// it's false, and returns it as an array of bytes.

      public synchronized byte[] get(String remoteFile, boolean doBinary)
      throws IOException
      {

// If transferring in binary mode, send a type command for type I (IMAGE)
            if (doBinary) {
                  String response = doCommand("TYPE I");
                  if (response.charAt(0) != '2') {
                        throw new IOException(response);
                  }
// If transferring in ascii mode, send a type command for type A (ASCII)
            } else {
                  String response = doCommand("TYPE A");
                  if (response.charAt(0) != '2') {
                        throw new IOException(response);
                  }
            }

// Set up the data connection

            Socket getSock = doPasvPort();

// Tell the server to send the file over
            String response = doCommand("RETR "+remoteFile);

// If the request is successful, the server should send a response
// in the 100's and then start sending the file. Once the file is
// sent, it should send a response in the 200's.

// Check for an initial response in the 100's

            if (response.charAt(0) != '1') {
                  getSock.close();
                  throw new IOException(response);
            }

            ByteArrayOutputStream out = new ByteArrayOutputStream();

// For binary transfers, read one byte at a time and store it in
// the array.
            if (doBinary) {
                  InputStream in = getSock.getInputStream();

                  int ch;

                  while ((ch = in.read()) >= 0) {
                        out.write(ch);
                  }

// For ascii transfers, read a line at a time and strip off whatever
// newline we got.
            } else {
                  DataInputStream in = new DataInputStream(
                        getSock.getInputStream());
                  PrintStream dataOut = new PrintStream(out);

                  String line;

                  while ((line = in.readLine()) != null) {
                        dataOut.println(line);
                  }
            }

// Close the data connection
            getSock.close();

// Make sure we got a response in the 200's saying the transfer was
// successful.
            response = getResponse();
            if (response.charAt(0) != '2') {
                  throw new IOException(response);
            }

            return out.toByteArray();
      }

// Stores an array of bytes in the named file using binary mode

      public synchronized void put(String remoteFile, byte[] data)
      throws IOException
      {
            put(remoteFile, data, true);      // default to binary
      }

// Stores an array of bytes in the named file, using binary mode if
// doBinary is true, ascii mode otherwise.

      public synchronized void put(String remoteFile, byte[] data,
            boolean doBinary)
      throws IOException
      {

// If transferring in binary mode, send a type command for type I (IMAGE)
            if (doBinary) {
                  String response = doCommand("TYPE I");
                  if (response.charAt(0) != '2') {
                        throw new IOException(response);
                  }

// If transferring in ascii mode, send a type command for type A (ASCII)
            } else {
                  String response = doCommand("TYPE A");
                  if (response.charAt(0) != '2') {
                        throw new IOException(response);
                  }
            }

// Open up a data connection
            Socket putSock = doPasvPort();

// Tell the server where we want it to store the data we are sending

            String response = doCommand("STOR "+remoteFile);

// If the request is successful, the server should send a response
// in the 100's and then start receiving the bytes. Once the data
// connection is closed, it should send a response in the 200's.

            if (response.charAt(0) != '1') {
                  putSock.close();
                  throw new IOException(response);
            }

// If binary mode, just write all the bytes
            if (doBinary) {
                  OutputStream out = putSock.getOutputStream();

                  out.write(data);

// If ascii mode, write the data a line at a time

            } else {
                  DataInputStream in = new DataInputStream(
                        new ByteArrayInputStream(data));
                  DataOutputStream out = new DataOutputStream(
                        putSock.getOutputStream());

                  String line;

                  while ((line = in.readLine()) != null) {
                        out.writeBytes(line+"\r");
                  }
            }

            putSock.close();

            response = getResponse();

// Make sure we got a 200 response

            if (response.charAt(0) != '2') {
                  throw new IOException(response);
            }
      }
}
                  
Avatar of tom_corc3

ASKER

Hi Manhthang
Thanks for the code.
Is this suitable for an NT workstation and server and a Windows 98 machine using dial up networking.
Should I manually start up a dial up networking session before running the program?
Also a comment says 'Your server must support the PASV command'. How can I find this out? I don't know what the PASV command is.

Thanks
Tom
Hi there again manhthang
Forgive my ignorance about ftp.
Can you give me examples of these parameters into the constructors:

-String host ("ftp://10.24.34.198" ???)
-acct ????
-logon -  What effect does true and false have here?

Thanks
Tom
Adjusted points from 200 to 300
Does anyone know how to do this:
I open up an ftp link via dial up networking. The ftp site is only available by dialing this particular phone number. Dial up networking provides the ID and password necessary to connect to the site. When the dial up link is up I can browse to the site in my IE5 browser.
However in my Java program I get an 'Unknown Host Exception' on this line:
Socket sessionSock;
sessionSock = new Socket(host, port);

Here host is something like "ftp://100.100.100.100" and port is 21.

Has anyone got any ideas what I have to do to connect in Java?
Thanks
Tom
Sorry Tom about the late reply.
Yes this code can be used on all the platforms you mentioned because it's 100% Java. You need to resort to other programs (may be JNI) to invoke dialup networking inside your code. After that, use:

FTPSession sess = new FTPSession(
                    "10.24.34.198",
                    "anonymous", "tom@", true);
The flag logon is to log on to the server using the user name and password, after having opened an ftp session with the server. Usually if you put logon as false, you only get as far as the prompt for user name and password, and you will need to manually enter your name and password at the prompt.
The PASV command is like the PORT command, used to force the server to create a listen socket and hence establish a connection with the client.
To see whether this is available on your FTP server, open your server and type ? for a list of all available commands.
I think you got the error because you included "ftp://" at the front of your host string.
Hope this helps.
Thanks to both of you mplungjan and manhthang.
manhthang dropping the "ftp" worked.

mplungjan I am successfully using the Kasper classes and I am able to transfer file to and from the siteHowever th list and nlst copmmands return null pointer exceptions. Do you know why?
Here are the details:
500 Invalid PORT Command.
java.lang.NullPointerException
        at kasper.net.ftp.FTPClient.nlst(FTPClient.java, Comp
        at kasper.net.ftp.FTPClient.nlst(FTPClient.java)
        at FTPTestList.main(FTPTestList.java, Compiled Code)

Thanks
Tom
Adjusted points from 300 to 500
PLEASE
help me on this. I want to set the timeout for the call:
client.retr(filename)
if the file name does exist the program hangs. As the listing is unavailable (see my previous comment) I need to look for files that may not be there.
If I could set the timeout then it would be OK - ie move to the next file in the list.
Thanks in advance
Tom
ASKER CERTIFIED SOLUTION
Avatar of Michel Plungjan
Michel Plungjan
Flag of Denmark image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Comment accepted as answer
Thanks to both of you manhthang and mplungjan.
If I could split the 500 points between the 2 of you I would.
I eventually used mostly mplungjan's solution.
If either of you have an answer to the question of getting a complete directory listing on the remote server (other than Kaspers list or nlist) I'd appreciate if you you could mail me on tom_corc@homail.com
You could... Next time ask Customer services to help :-)

I will be back from vacation in two weeks - I will attempt to help you then if you have not found an answer in the mean time...

Michel
Can you do a list from a plain ftp session???

Michel