Java FTPClient (apache commons) storefile() successfully uploads file, then times out..?

brokethebuildagain picture brokethebuildagain · May 1, 2014 · Viewed 8.6k times · Source

Here's some code I have to upload a file to an FTP server:

private boolean ftpSend(byte[] fileBytes, String filename, HashMap<String, String> endpointAddressData) throws Exception {
    boolean success = false;

    String login = endpointAddressData.get("user");
    String hostname = endpointAddressData.get("host");
    String password = endpointAddressData.get("password");

    String portString = endpointAddressData.get("port");
    int port = (portString == null) ? 21 : Integer.parseInt(portString);

    FTPClient ftpClient = new FTPClient();
    ByteArrayInputStream inputStream = null;
    try {
        ftpClient.setConnectTimeout(10000);
        ftpClient.connect(hostname, port);

        int reply = ftpClient.getReplyCode();

        if(!FTPReply.isPositiveCompletion(reply)) {
            ftpClient.disconnect();

            throw new Exception("FTP server " + hostname + " refused connection.");
        }

        if (password != null) {
            ftpClient.login(login, password);
        } else {
            ftpClient.user(login);
        }

        logger_.debug("FTP - Writing: " + ftpClient.printWorkingDirectory() + "/" + filename + " on host " + endpointAddressData.get("host"));

        inputStream = new ByteArrayInputStream(fileBytes);
        success = ftpClient.storeFile(filename, inputStream);
        inputStream.close();
        success = success && ftpClient.completePendingCommand();

        logger_.debug("FTP - Completed: " + ftpClient.printWorkingDirectory() + "/" + filename + " on host " + endpointAddressData.get("host"));

        ftpClient.disconnect();

    } catch (Exception ex) {
        success = false;

        logger_.error("An error occurred during FTP upload to " + endpointAddressData.get("host") + ".", ex);
    } finally {
        if (ftpClient.isConnected()) {
            ftpClient.abort();
            ftpClient.disconnect();
        }

        if (inputStream != null) {
            inputStream.close();
        }

        return success;
    }
}

When I run it with some test data, the upload clearly completes successfully (here's the FTP server log):

(000009)01/05/2014 12:13:09 PM - (not logged in) (127.0.0.1)> USER ######
(000009)01/05/2014 12:13:09 PM - (not logged in) (127.0.0.1)> 331 Password required for ######
(000009)01/05/2014 12:13:09 PM - (not logged in) (127.0.0.1)> PASS ########
(000009)01/05/2014 12:13:09 PM - anonymous (127.0.0.1)> 230 Logged on
(000009)01/05/2014 12:13:09 PM - anonymous (127.0.0.1)> PWD ########
(000009)01/05/2014 12:13:09 PM - anonymous (127.0.0.1)> 257 "/" is current directory.
(000009)01/05/2014 12:13:09 PM - anonymous (127.0.0.1)> PORT 127,0,0,1,250,242
(000009)01/05/2014 12:13:09 PM - anonymous (127.0.0.1)> 200 Port command successful
(000009)01/05/2014 12:13:09 PM - anonymous (127.0.0.1)> STOR testFTP1.txt
(000009)01/05/2014 12:13:09 PM - anonymous (127.0.0.1)> 150 Opening data channel for file upload to server of "/testFTP1.txt"
(000009)01/05/2014 12:13:09 PM - anonymous (127.0.0.1)> 226 Successfully transferred "/testFTP1.txt"
(000009)01/05/2014 12:15:10 PM - anonymous (127.0.0.1)> 421 Connection timed out.
(000009)01/05/2014 12:15:10 PM - anonymous (127.0.0.1)> disconnected.

What am I doing wrong?

Answer

brokethebuildagain picture brokethebuildagain · May 1, 2014

After stepping through in the debugger, I found out that the ftpClient.completePendingCommand() call was causing the timeout. I guess the command was already completed and it was waiting for the next command indefinitely. I commented out that line and the code completed easily.

From the JavaDoc on FTPClient: http://commons.apache.org/proper/commons-net/apidocs/org/apache/commons/net/ftp/FTPClient.html#completePendingCommand()

There are a few FTPClient methods that do not complete the entire sequence of FTP commands to complete a transaction. These commands require some action by the programmer after the reception of a positive intermediate command. After the programmer's code completes its actions, it must call this method to receive the completion reply from the server and verify the success of the entire transaction.

I guess storeFile() (the FTP STOR command) isn't one of those methods.