Search code examples
javaftpfile-transferftp-clientapache-commons-net

Java FTPClient Stuck


I'm trying to download a file from my ftp server, but it gets stuck while retrieving them. I'm using commons-net-3.6.jar

Things I've Noticed

When I use ftpClient.enterRemotePassiveMode();
I can see in the FileZilla server interface that the progress of the download for the connection stuck at 76% (688,128 bytes)

When I use ftpClient.enterLocalPassiveMode();
I can see in the FileZilla server interface that the progress of the download for the connection stuck at 96% (884,736 bytes)

What's interesting is that in these two modes it always gets stuck at that exact same amount of bytes no matter what the file is. If the file size is larger than 884,736 or 688,128 it just gets stuck.

It works perfectly for files less that 884,736 bytes in LocalPassiveMode and 688,128 bytes in RemotePassiveMode.

I've tried downloading from a different server and it didn't work so its definitely not a server related issue.

My Code

public class FTPDownload {

private List<BufferedImage> imageList;
private boolean wasConnected;


public void getFiles(String orderRootDirectory){

    wasConnected = true;

    imageList = new ArrayList<>();
    FTPConnection con = new FTPConnection(); // to get the FTP Credentials
    con.readData();
   try {
       FTPClient ftpClient = new FTPClient();
       ftpClient.connect(con.getServerIp());
       ftpClient.enterLocalPassiveMode();

       ftpClient.login(con.getUsername(), con.getPassword());
       ftpClient.setAutodetectUTF8(true);
       ftpClient.setBufferSize(1024 * 1024); // tried with and without this no luck there

       wasConnected = ftpClient.isConnected();

       FTPFile[] files = ftpClient.listFiles(orderRootDirectory);

       for (FTPFile file : files) {
           String details = file.getName();


           if (file.isDirectory()) {
               details = "[" + details + "]";
           }

           String totalFilePath = orderRootDirectory+"/"+file.getName();



           InputStream inputStream = ftpClient.retrieveFileStream(totalFilePath);  // stuck over here

           System.out.println(ftpClient.completePendingCommand());
           System.out.println(ftpClient.getReplyString());

           System.out.println("Reading File...");
           long start=System.currentTimeMillis();
           ImageIO.setUseCache(false);
           BufferedImage bimg = ImageIO.read(inputStream);


           long end=System.currentTimeMillis();
           System.out.println("time="+(end-start));


           imageList.add(bimg);

           details += "\t\t" + file.getSize();
           details += "\t\t" + file.getName();
           System.out.println(details);
       }

       System.out.println(imageList.size());

       ftpClient.logout();
       ftpClient.disconnect();
   }catch (Exception e){

       e.printStackTrace();
       wasConnected = false;

   }


}

Solution

  • FTPClient#retrieveFileStream states that:

    You must close the InputStream when you finish reading from it. The InputStream itself will take care of closing the parent data connection socket upon being closed.

    And

    To finalize the file transfer you must call completePendingCommand and check its return value to verify success. If this is not done, subsequent commands may behave unexpectedly.


    Calls should be finalized using completePendingCommand, right now you're executing that method before finalizing (you're reading from the inputStream after you've called completePendingCommand).

    Also you're not closing your inputStream which it specifically states.


    To fix your issues do something like this, where we first close the inputStream and then call completePendingCommand, all of this should happen after we've read the inputStream.

    InputStream inputStream = ftpClient.retrieveFileStream(totalFilePath);
    
    BufferedImage bimg = ImageIO.read(inputStream);
    
    inputStream.close();
    if (!ftpClient.completePendingCommand()) {
        // Throw some error or do something, file transfer failed
    }