Java FTP Client Utility with Commons-Net

This article presents a Java utility class for interacting with FTP servers, leveraging the Apache Commons-Net library. It covers esential operations such as connecting, uploading, downloading, and deleting files.

A common challenge when working with FTP is handling remote directory creation, as there isn't a direct API to check for directory exsitence. The provided solution iterates through path components to ensure directories are created recursively if they don't already exist.

1. Maven Dependency


<!-- Apache Commons-Net for FTP operations -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.6</version> <!-- Use a recent stable version -->
</dependency>

2. FTP Utility Class (FtpClientUtil.java)


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

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.File;
import java.net.ConnectException;

public class FtpClientUtil {

    private static FTPClient ftpClient = new FTPClient();

    /**
     * Establishes a connection and logs into the FTP server.
     *
     * @param hostname The FTP server hostname or IP address.
     * @param port     The FTP server port (usually 21).
     * @param username The username for authentication.
     * @param password The password for authentication.
     * @throws IOException If a connection or login error occurs.
     */
    private static void connectAndLogin(String hostname, int port, String username, String password) throws IOException {
        if (!ftpClient.isConnected()) {
            try {
                ftpClient.connect(hostname, port);
                int replyCode = ftpClient.getReplyCode();
                if (!FTPReply.isPositiveCompletion(replyCode)) {
                    ftpClient.disconnect();
                    throw new ConnectException("FTP server refused connection. Reply code: " + replyCode);
                }
            } catch (ConnectException e) {
                throw new ConnectException("Failed to connect to FTP server: " + hostname + ":" + port + ". " + e.getMessage());
            } catch (IOException e) {
                throw new IOException("Error during FTP connection to " + hostname + ":" + port + ". " + e.getMessage());
            }
        }

        if (!ftpClient.login(username, password)) {
            throw new IOException("FTP login failed for user: " + username);
        }

        // Enable UTF-8 encoding for file names
        ftpClient.setControlEncoding("UTF-8");
        // Use passive mode for better firewall compatibility
        ftpClient.enterLocalPassiveMode();
        // Set the transfer mode to binary
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
    }

    /**
     * Uploads a file to the FTP server.
     *
     * @param hostname     The FTP server hostname.
     * @param port         The FTP server port.
     * @param username     The FTP username.
     * @param password     The FTP password.
     * @param remotePath   The destination directory on the FTP server.
     * @param remoteName   The name of the file on the FTP server.
     * @param inputStream  The input stream of the file to upload.
     * @return true if the upload was successful, false otherwise.
     * @throws IOException If an I/O error occurs during the upload.
     */
    public static boolean uploadFile(String hostname, int port, String username, String password, String remotePath, String remoteName, InputStream inputStream) throws IOException {
        boolean success = false;
        try {
            connectAndLogin(hostname, port, username, password);
            createRemoteDirectory(remotePath);
            // Change to the target directory
            if (ftpClient.changeWorkingDirectory(remotePath)) {
                success = ftpClient.storeFile(remoteName, inputStream);
            } else {
                System.err.println("Failed to change directory to: " + remotePath);
            }
        } finally {
            closeResources(inputStream, null);
            disconnect();
        }
        return success;
    }

    /**
     * Downloads a file from the FTP server.
     *
     * @param hostname    The FTP server hostname.
     * @param port        The FTP server port.
     * @param username    The FTP username.
     * @param password    The FTP password.
     * @param remotePath  The directory of the file on the FTP server.
     * @param remoteName  The name of the file to download.
     * @param localPath   The local directory to save the downloaded file.
     * @return true if the download was successful, false otherwise.
     * @throws IOException If an I/O error occurs during the download.
     */
    public static boolean downloadFile(String hostname, int port, String username, String password, String remotePath, String remoteName, String localPath) throws IOException {
        boolean success = false;
        OutputStream outputStream = null;
        try {
            connectAndLogin(hostname, port, username, password);
            if (ftpClient.changeWorkingDirectory(remotePath)) {
                FTPFile[] files = ftpClient.listFiles();
                for (FTPFile file : files) {
                    if (remoteName.equalsIgnoreCase(file.getName())) {
                        File localFile = new File(localPath, file.getName());
                        outputStream = new FileOutputStream(localFile);
                        success = ftpClient.retrieveFile(file.getName(), outputStream);
                        break;
                    }
                }
            } else {
                System.err.println("Failed to change directory to: " + remotePath);
            }
        } finally {
            closeResources(null, outputStream);
            disconnect();
        }
        return success;
    }

    /**
     * Deletes a file from the FTP server.
     *
     * @param hostname   The FTP server hostname.
     * @param port       The FTP server port.
     * @param username   The FTP username.
     * @param password   The FTP password.
     * @param remotePath The directory of the file on the FTP server.
     * @param remoteName The name of the file to delete.
     * @return true if the deletion was successful, false otherwise.
     * @throws IOException If an I/O error occurs during deletion.
     */
    public static boolean deleteFile(String hostname, int port, String username, String password, String remotePath, String remoteName) throws IOException {
        boolean success = false;
        try {
            connectAndLogin(hostname, port, username, password);
            if (ftpClient.changeWorkingDirectory(remotePath)) {
                success = ftpClient.deleteFile(remoteName);
            } else {
                System.err.println("Failed to change directory to: " + remotePath);
            }
        } finally {
            disconnect();
        }
        return success;
    }

    /**
     * Creates remote directories recursively if they do not exist.
     *
     * @param remoteDirectoryPath The full path of the directory to create.
     * @throws IOException If an I/O error occurs.
     */
    private static void createRemoteDirectory(String remoteDirectoryPath) throws IOException {
        if (remoteDirectoryPath == null || remoteDirectoryPath.isEmpty()) {
            return;
        }

        // Ensure we are in the root directory initially for relative path processing
        if (!ftpClient.changeWorkingDirectory("/")) {
            throw new IOException("Failed to change to root directory.");
        }

        String[] dirs = remoteDirectoryPath.split("/");
        for (String dir : dirs) {
            if (dir.isEmpty()) continue; // Skip empty parts from leading/trailing slashes

            // Check if directory exists
            FTPFile[] files = ftpClient.listFiles(dir);
            boolean dirExists = false;
            for (FTPFile file : files) {
                if (file.isDirectory() && dir.equals(file.getName())) {
                    dirExists = true;
                    break;
                }
            }

            if (!dirExists) {
                if (!ftpClient.makeDirectory(dir)) {
                    throw new IOException("Failed to create directory: " + dir);
                }
            }

            // Change into the directory
            if (!ftpClient.changeWorkingDirectory(dir)) {
                throw new IOException("Failed to change into directory: " + dir);
            }
        }
    }

    /**
     * Closes the FTP client connection.
     */
    private static void disconnect() {
        if (ftpClient.isConnected()) {
            try {
                ftpClient.logout();
                ftpClient.disconnect();
            } catch (IOException e) {
                System.err.println("Error during FTP disconnect: " + e.getMessage());
            }
        }
    }

    /**
     * Closes input and output streams.
     */
    private static void closeResources(InputStream is, OutputStream os) {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                System.err.println("Error closing input stream: " + e.getMessage());
            }
        }
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
                System.err.println("Error closing output stream: " + e.getMessage());
            }
        }
    }
}

3. Usage Example

To use the FtpClientUtil, instantiate it and call the relevant methods, providing the necessary connection details and file paths.


// Example for uploading a file
String ftpHost = "your_ftp_host";
int ftpPort = 21;
String ftpUser = "your_username";
String ftpPass = "your_password";
String remoteDir = "/path/to/upload";
String remoteFileName = "my_uploaded_file.txt";
InputStream fileStream = new FileInputStream("local_file_to_upload.txt");

try {
    boolean uploaded = FtpClientUtil.uploadFile(ftpHost, ftpPort, ftpUser, ftpPass, remoteDir, remoteFileName, fileStream);
    if (uploaded) {
        System.out.println("File uploaded successfully!");
    } else {
        System.out.println("File upload failed.");
    }
} catch (IOException e) {
    System.err.println("An error occurred during upload: " + e.getMessage());
    e.printStackTrace();
}

// Example for downloading a file
String localDownloadPath = "/path/to/save/locally";
String remoteFileNameToDownload = "remote_file.dat";
try {
    boolean downloaded = FtpClientUtil.downloadFile(ftpHost, ftpPort, ftpUser, ftpPass, remoteDir, remoteFileNameToDownload, localDownloadPath);
    if (downloaded) {
        System.out.println("File downloaded successfully!");
    } else {
        System.out.println("File download failed.");
    }
} catch (IOException e) {
    System.err.println("An error occurred during download: " + e.getMessage());
    e.printStackTrace();
}

// Example for deleting a file
try {
    boolean deleted = FtpClientUtil.deleteFile(ftpHost, ftpPort, ftpUser, ftpPass, remoteDir, remoteFileName);
    if (deleted) {
        System.out.println("File deleted successfully!");
    } else {
        System.out.println("File deletion failed.");
    }
} catch (IOException e) {
    System.err.println("An error occurred during deletion: " + e.getMessage());
    e.printStackTrace();
}

Tags: java FTP commons-net file-transfer network-programming

Posted on Fri, 15 May 2026 22:57:51 +0000 by mmosel