简体   繁体   中英

How to solve exception with “org.apache.commons.net.ftp.FTPClient”

Im not sure if this is a similar problem as I faced since it doesn't state an Exception: Issue with org.apache.commons.net.ftp.FTPClient listFiles()

I'm trying to use this class FTPUtil to download the root folder: https://www.codejava.net/java-se/ftp/how-to-download-a-complete-folder-from-a-ftp-server

at the line FTPFile[] subFiles = ftpClient.listFiles(dirToList); I get this Exception:

 java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.InetAddress.getHostAddress()' on a null object reference
    at org.apache.commons.net.ftp.FTPClient._openDataConnection_(FTPClient.java:938)
    at org.apache.commons.net.ftp.FTPClient._openDataConnection_(FTPClient.java:785)
    at org.apache.commons.net.ftp.FTPClient.initiateListParsing(FTPClient.java:3409)
    at org.apache.commons.net.ftp.FTPClient.initiateListParsing(FTPClient.java:3339)
    at org.apache.commons.net.ftp.FTPClient.listFiles(FTPClient.java:3016)
    at com.censored.FTPUtil.downloadDirectory(FTPUtil.java:39)

I don't really understand which Object is null and why. As String parentDir I tried "" and "\\" both with the same result I don't think the input is wrong.
The FTP server is running fine I see that my program logs in successful and file zilla can also access the folder.

EDIT:

Here is the class from the link I'm using:

package com.censored;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;

/**
 * This utility class implements a method that downloads a directory completely
 * from a FTP server, using Apache Commons Net API.
 *
 * @author www.codejava.net
 *
 */
public class FTPUtil {

    /**
     * Download a whole directory from a FTP server.
     * @param ftpClient an instance of org.apache.commons.net.ftp.FTPClient class.
     * @param parentDir Path of the parent directory of the current directory being
     * downloaded.
     * @param currentDir Path of the current directory being downloaded.
     * @param saveDir path of directory where the whole remote directory will be
     * downloaded and saved.
     * @throws IOException if any network or IO error occurred.
     */
    public static void downloadDirectory(FTPClient ftpClient, String parentDir,
            String currentDir, String saveDir) throws IOException {
        String dirToList = parentDir;
        if (!currentDir.equals("")) {
            dirToList += "/" + currentDir;
        }

        FTPFile[] subFiles = ftpClient.listFiles(dirToList); //This line gives the Exception

        if (subFiles != null && subFiles.length > 0) {
            for (FTPFile aFile : subFiles) {
                String currentFileName = aFile.getName();
                if (currentFileName.equals(".") || currentFileName.equals("..")) {
                    // skip parent directory and the directory itself
                    continue;
                }
                String filePath = parentDir + "/" + currentDir + "/"
                        + currentFileName;
                if (currentDir.equals("")) {
                    filePath = parentDir + "/" + currentFileName;
                }

                String newDirPath = saveDir + parentDir + File.separator
                        + currentDir + File.separator + currentFileName;
                if (currentDir.equals("")) {
                    newDirPath = saveDir + parentDir + File.separator
                              + currentFileName;
                }

                if (aFile.isDirectory()) {
                    // create the directory in saveDir
                    File newDir = new File(newDirPath);
                    boolean created = newDir.mkdirs();
                    if (created) {
                        System.out.println("CREATED the directory: " + newDirPath);
                    } else {
                        System.out.println("COULD NOT create the directory: " + newDirPath);
                    }

                    // download the sub directory
                    downloadDirectory(ftpClient, dirToList, currentFileName,
                            saveDir);
                } else {
                    // download the file
                    boolean success = downloadSingleFile(ftpClient, filePath,
                            newDirPath);
                    if (success) {
                        System.out.println("DOWNLOADED the file: " + filePath);
                    } else {
                        System.out.println("COULD NOT download the file: "
                                + filePath);
                    }
                }
            }
        }
    }

    /**
     * Download a single file from the FTP server
     * @param ftpClient an instance of org.apache.commons.net.ftp.FTPClient class.
     * @param remoteFilePath path of the file on the server
     * @param savePath path of directory where the file will be stored
     * @return true if the file was downloaded successfully, false otherwise
     * @throws IOException if any network or IO error occurred.
     */
    public static boolean downloadSingleFile(FTPClient ftpClient,
            String remoteFilePath, String savePath) throws IOException {
        File downloadFile = new File(savePath);

        File parentDir = downloadFile.getParentFile();
        if (!parentDir.exists()) {
            parentDir.mkdir();
        }

        OutputStream outputStream = new BufferedOutputStream(
                new FileOutputStream(downloadFile));
        try {
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            return ftpClient.retrieveFile(remoteFilePath, outputStream);
        } catch (IOException ex) {
            throw ex;
        } finally {
            if (outputStream != null) {
                outputStream.close();
            }
        }
    }
}

And this my code calling it (censored the unrelevant stuff I dont have the permission to show the full class):

val server = "10.0.2.2"  // Because of Emulator
val port = 8080 //yeah I know not standard port, this will change
val user = "censored"
val pass = "censored"        

val remoteDirPath = ""   //also tried "/"
val saveDirPath = File(myFilesDir, "fromFTP/").absolutePath   //ignore myFilesDir the Debuger shows that its set correct and it isnt relevant before the Exception

val ftpClient = FTPClient()



    try {
        // connect and login to the server
        ftpClient.connect(server, port)
        ftpClient.login(user, pass)

        // use local passive mode to pass firewall
        ftpClient.enterLocalPassiveMode()

        println("Connected")

        FTPUtil.downloadDirectory(ftpClient, remoteDirPath, "", saveDirPath) //this line is the one giving the exception

        // log out and disconnect from the server
        ftpClient.logout()
        ftpClient.disconnect()

        println("Disconnected")
    } catch (ex: IOException) {
        ex.printStackTrace()
    }

Update: During Debuging I found that this if in class FTPClient extends FTP is true:

 if (__remoteVerificationEnabled && !verifyRemote(socket))
        {
            socket.close();

            throw new IOException(
                    "Host attempting data connection " + socket.getInetAddress().getHostAddress() +
                    " is not same as server " + getRemoteAddress().getHostAddress());
        }

        return socket;

I think socket.getInetAddress() causes an exception (inside of the outer exception) because the Socket is already closed? This would mean that the FTPClient itself contains a bug? However I think the solution for my problem is that the if (__remoteVerificationEnabled && !verifyRemote(socket)) is true, so somehow this if must become false. Why is it true? What kind of verification is this?

Ok I think it is just a workaround but the solution seams to be to ad this line: ftpClient.setRemoteVerificationEnabled(false);

Read the Update part in the question to see how I found this solution.

I think you're correct. This is a bug in FTPClient.

socket.getInetAddress() is documented to throw NullPointerException if the socket is not open. This is called right after we call socket.close().

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM