Java FTP Client for File Upload, Download, and Directory Listing
Using Apache Commons Net, Java applications can interact with FTP servers to upload, download, and list files. The following utilities demonstrtae core operations using FTPClient.
package com.li.utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
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;
import org.apache.commons.net.ftp.FTPReply;
public class FtpUtil {
public static boolean uploadFile(String host, int port, String user, String pass,
String baseDir, String subPath, String targetName, InputStream dataStream) {
FTPClient client = new FTPClient();
try {
client.connect(host, port);
if (!FTPReply.isPositiveCompletion(client.getReplyCode())) {
client.disconnect();
return false;
}
if (!client.login(user, pass)) {
client.disconnect();
return false;
}
client.enterLocalPassiveMode();
client.setFileType(FTP.BINARY_FILE_TYPE);
String fullPath = baseDir + subPath;
if (!client.changeWorkingDirectory(fullPath)) {
String[] parts = subPath.split("/");
String currentPath = baseDir;
for (String part : parts) {
if (part == null || part.isEmpty()) continue;
currentPath += "/" + part;
if (!client.changeWorkingDirectory(currentPath)) {
if (!client.makeDirectory(currentPath)) {
return false;
}
client.changeWorkingDirectory(currentPath);
}
}
}
boolean stored = client.storeFile(targetName, dataStream);
dataStream.close();
client.logout();
return stored;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
if (client.isConnected()) {
try {
client.disconnect();
} catch (IOException ex) {
// ignore
}
}
}
}
public static boolean downloadFile(String host, int port, String user, String pass,
String remoteDir, String fileName, String localDir) {
FTPClient client = new FTPClient();
try {
client.connect(host, port);
if (!FTPReply.isPositiveCompletion(client.getReplyCode())) {
client.disconnect();
return false;
}
if (!client.login(user, pass)) {
client.disconnect();
return false;
}
client.enterLocalPassiveMode();
if (!client.changeWorkingDirectory(remoteDir)) {
return false;
}
FTPFile[] files = client.listFiles();
for (FTPFile file : files) {
if (file.isFile() && file.getName().equals(fileName)) {
File localFile = new File(localDir, fileName);
try (OutputStream out = new FileOutputStream(localFile)) {
client.retrieveFile(fileName, out);
}
client.logout();
return true;
}
}
client.logout();
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
if (client.isConnected()) {
try {
client.disconnect();
} catch (IOException ex) {
// ignore
}
}
}
}
}
For listing directory contents recursively:
package com.li.utils;
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.util.ArrayList;
import java.util.List;
public class FTPDirectoryLister {
private final FTPClient ftp;
private final List<String> filePaths;
public FTPDirectoryLister() {
this.ftp = new FTPClient();
this.filePaths = new ArrayList<>();
}
public boolean connectAndLogin(String host, int port, String username, String password) throws IOException {
ftp.connect(host, port);
if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
ftp.disconnect();
return false;
}
boolean loggedIn = ftp.login(username, password);
if (loggedIn) {
ftp.enterLocalPassiveMode();
ftp.setControlEncoding("UTF-8");
} else {
ftp.disconnect();
}
return loggedIn;
}
public void listFilesRecursively(String dirPath) throws IOException {
if (!dirPath.endsWith("/")) {
dirPath += "/";
}
ftp.changeWorkingDirectory(dirPath);
FTPFile[] items = ftp.listFiles();
for (FTPFile item : items) {
if (item.isFile()) {
filePaths.add(dirPath + item.getName());
} else if (item.isDirectory()) {
filePaths.add(dirPath + item.getName() + "/");
// Uncomment to recurse into subdirectories:
// listFilesRecursively(dirPath + item.getName());
}
}
}
public List<String> getFilePaths() {
return new ArrayList<>(filePaths);
}
public void disconnect() throws IOException {
if (ftp.isConnected()) {
ftp.disconnect();
}
}
}
When the FTP user's root is /home/uftp, paths passed to changeWorkingDirectory() should be relative to that root. For example, to access /home/uftp/public, use changeWorkingDirectory("/public").
Passive mode (enterLocalPassiveMode()) is typically required to avoid firewall issues on the client side.