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();
}