FTP Client Implementation in Java

Introduction

A comprehensive FTP client implementation in Java that supports both standard FTP and secure FTPS protocols. This implementation provides file upload, download, directory listing, and various FTP operations with robust error handling and progress tracking.

Core FTP Client Implementation

Main FTP Client Class

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.regex.*;
public class FTPClient {
private Socket controlSocket;
private BufferedReader reader;
private PrintWriter writer;
private String host;
private int port;
private String username;
private String password;
private boolean connected;
private boolean passiveMode;
private int timeout;
// FTP response codes
private static final int RESPONSE_READY = 220;
private static final int RESPONSE_USER_OK = 331;
private static final int RESPONSE_LOGIN_OK = 230;
private static final int RESPONSE_PASV_OK = 227;
private static final int RESPONSE_PORT_OK = 200;
private static final int RESPONSE_LIST_OK = 150;
private static final int RESPONSE_TRANSFER_COMPLETE = 226;
private static final int RESPONSE_DIRECTORY_OK = 257;
private static final int RESPONSE_FILE_ACTION_OK = 250;
public FTPClient() {
this.connected = false;
this.passiveMode = true; // Use passive mode by default
this.timeout = 30000; // 30 seconds default timeout
}
public void connect(String host, int port) throws FTPException {
try {
this.host = host;
this.port = port;
controlSocket = new Socket();
controlSocket.connect(new InetSocketAddress(host, port), timeout);
controlSocket.setSoTimeout(timeout);
reader = new BufferedReader(new InputStreamReader(controlSocket.getInputStream()));
writer = new PrintWriter(controlSocket.getOutputStream(), true);
// Read welcome message
FTPResponse response = readResponse();
if (response.getCode() != RESPONSE_READY) {
throw new FTPException("Failed to connect: " + response.getMessage());
}
connected = true;
System.out.println("Connected to " + host + ":" + port);
} catch (IOException e) {
throw new FTPException("Connection failed: " + e.getMessage(), e);
}
}
public void connect(String host) throws FTPException {
connect(host, 21); // Default FTP port
}
public void login(String username, String password) throws FTPException {
if (!connected) {
throw new FTPException("Not connected to FTP server");
}
this.username = username;
this.password = password;
try {
// Send username
sendCommand("USER " + username);
FTPResponse userResponse = readResponse();
if (userResponse.getCode() != RESPONSE_USER_OK) {
throw new FTPException("Username not accepted: " + userResponse.getMessage());
}
// Send password
sendCommand("PASS " + password);
FTPResponse passResponse = readResponse();
if (passResponse.getCode() != RESPONSE_LOGIN_OK) {
throw new FTPException("Login failed: " + passResponse.getMessage());
}
System.out.println("Logged in as " + username);
} catch (IOException e) {
throw new FTPException("Login failed: " + e.getMessage(), e);
}
}
public void logout() throws FTPException {
if (!connected) {
return;
}
try {
sendCommand("QUIT");
readResponse(); // Read goodbye message
} catch (IOException e) {
// Ignore errors during logout
} finally {
disconnect();
}
}
public void disconnect() {
connected = false;
try {
if (reader != null) reader.close();
if (writer != null) writer.close();
if (controlSocket != null) controlSocket.close();
} catch (IOException e) {
System.err.println("Error during disconnect: " + e.getMessage());
}
System.out.println("Disconnected from FTP server");
}
public void setPassiveMode(boolean passive) {
this.passiveMode = passive;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
if (controlSocket != null) {
try {
controlSocket.setSoTimeout(timeout);
} catch (SocketException e) {
System.err.println("Failed to set timeout: " + e.getMessage());
}
}
}
// File operations
public void uploadFile(String localFilePath, String remoteFilePath) throws FTPException {
uploadFile(localFilePath, remoteFilePath, null);
}
public void uploadFile(String localFilePath, String remoteFilePath, ProgressListener listener) 
throws FTPException {
File localFile = new File(localFilePath);
if (!localFile.exists() || !localFile.isFile()) {
throw new FTPException("Local file does not exist: " + localFilePath);
}
try (FileInputStream fileInputStream = new FileInputStream(localFile);
InputStream progressInputStream = addProgressTracking(fileInputStream, localFile.length(), listener)) {
// Set transfer type to binary
setBinaryMode();
// Open data connection
Socket dataSocket = openDataConnection();
OutputStream dataOutputStream = dataSocket.getOutputStream();
// Send STOR command
sendCommand("STOR " + remoteFilePath);
FTPResponse response = readResponse();
if (response.getCode() != RESPONSE_LIST_OK && response.getCode() != 125) {
throw new FTPException("STOR command failed: " + response.getMessage());
}
// Transfer file data
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = progressInputStream.read(buffer)) != -1) {
dataOutputStream.write(buffer, 0, bytesRead);
}
dataOutputStream.flush();
dataOutputStream.close();
dataSocket.close();
// Check transfer completion
response = readResponse();
if (response.getCode() != RESPONSE_TRANSFER_COMPLETE) {
throw new FTPException("File transfer failed: " + response.getMessage());
}
System.out.println("Upload completed: " + localFilePath + " -> " + remoteFilePath);
} catch (IOException e) {
throw new FTPException("Upload failed: " + e.getMessage(), e);
}
}
public void downloadFile(String remoteFilePath, String localFilePath) throws FTPException {
downloadFile(remoteFilePath, localFilePath, null);
}
public void downloadFile(String remoteFilePath, String localFilePath, ProgressListener listener) 
throws FTPException {
File localFile = new File(localFilePath);
try (FileOutputStream fileOutputStream = new FileOutputStream(localFile);
OutputStream progressOutputStream = addProgressTracking(fileOutputStream, 0, listener)) {
// Set transfer type to binary
setBinaryMode();
// Open data connection
Socket dataSocket = openDataConnection();
InputStream dataInputStream = dataSocket.getInputStream();
// Send RETR command
sendCommand("RETR " + remoteFilePath);
FTPResponse response = readResponse();
if (response.getCode() != RESPONSE_LIST_OK && response.getCode() != 125) {
throw new FTPException("RETR command failed: " + response.getMessage());
}
// Transfer file data
byte[] buffer = new byte[4096];
int bytesRead;
long totalBytes = 0;
while ((bytesRead = dataInputStream.read(buffer)) != -1) {
progressOutputStream.write(buffer, 0, bytesRead);
totalBytes += bytesRead;
}
progressOutputStream.flush();
dataInputStream.close();
dataSocket.close();
// Update progress for total file size
if (listener != null) {
listener.onProgress(totalBytes, totalBytes, 100);
}
// Check transfer completion
response = readResponse();
if (response.getCode() != RESPONSE_TRANSFER_COMPLETE) {
throw new FTPException("File transfer failed: " + response.getMessage());
}
System.out.println("Download completed: " + remoteFilePath + " -> " + localFilePath);
} catch (IOException e) {
throw new FTPException("Download failed: " + e.getMessage(), e);
}
}
// Directory operations
public List<FTPFile> listFiles() throws FTPException {
return listFiles("");
}
public List<FTPFile> listFiles(String directory) throws FTPException {
try {
// Open data connection
Socket dataSocket = openDataConnection();
// Send LIST command
String command = directory.isEmpty() ? "LIST" : "LIST " + directory;
sendCommand(command);
FTPResponse response = readResponse();
if (response.getCode() != RESPONSE_LIST_OK && response.getCode() != 125) {
throw new FTPException("LIST command failed: " + response.getMessage());
}
// Read directory listing
BufferedReader dataReader = new BufferedReader(new InputStreamReader(dataSocket.getInputStream()));
List<FTPFile> files = new ArrayList<>();
String line;
while ((line = dataReader.readLine()) != null) {
FTPFile file = parseListLine(line);
if (file != null) {
files.add(file);
}
}
dataReader.close();
dataSocket.close();
// Check transfer completion
response = readResponse();
if (response.getCode() != RESPONSE_TRANSFER_COMPLETE) {
throw new FTPException("Directory listing failed: " + response.getMessage());
}
return files;
} catch (IOException e) {
throw new FTPException("Directory listing failed: " + e.getMessage(), e);
}
}
public void createDirectory(String directory) throws FTPException {
try {
sendCommand("MKD " + directory);
FTPResponse response = readResponse();
if (response.getCode() != RESPONSE_DIRECTORY_OK && response.getCode() != RESPONSE_FILE_ACTION_OK) {
throw new FTPException("Failed to create directory: " + response.getMessage());
}
System.out.println("Directory created: " + directory);
} catch (IOException e) {
throw new FTPException("Failed to create directory: " + e.getMessage(), e);
}
}
public void removeDirectory(String directory) throws FTPException {
try {
sendCommand("RMD " + directory);
FTPResponse response = readResponse();
if (response.getCode() != RESPONSE_FILE_ACTION_OK) {
throw new FTPException("Failed to remove directory: " + response.getMessage());
}
System.out.println("Directory removed: " + directory);
} catch (IOException e) {
throw new FTPException("Failed to remove directory: " + e.getMessage(), e);
}
}
public void changeDirectory(String directory) throws FTPException {
try {
sendCommand("CWD " + directory);
FTPResponse response = readResponse();
if (response.getCode() != RESPONSE_FILE_ACTION_OK) {
throw new FTPException("Failed to change directory: " + response.getMessage());
}
System.out.println("Changed to directory: " + directory);
} catch (IOException e) {
throw new FTPException("Failed to change directory: " + e.getMessage(), e);
}
}
public String getCurrentDirectory() throws FTPException {
try {
sendCommand("PWD");
FTPResponse response = readResponse();
if (response.getCode() != RESPONSE_DIRECTORY_OK) {
throw new FTPException("Failed to get current directory: " + response.getMessage());
}
// Extract directory from response (typically "257 "/path" is current directory")
String message = response.getMessage();
int firstQuote = message.indexOf('"');
int lastQuote = message.lastIndexOf('"');
if (firstQuote != -1 && lastQuote != -1 && firstQuote < lastQuote) {
return message.substring(firstQuote + 1, lastQuote);
}
return message;
} catch (IOException e) {
throw new FTPException("Failed to get current directory: " + e.getMessage(), e);
}
}
// File management operations
public void deleteFile(String remoteFilePath) throws FTPException {
try {
sendCommand("DELE " + remoteFilePath);
FTPResponse response = readResponse();
if (response.getCode() != RESPONSE_FILE_ACTION_OK) {
throw new FTPException("Failed to delete file: " + response.getMessage());
}
System.out.println("File deleted: " + remoteFilePath);
} catch (IOException e) {
throw new FTPException("Failed to delete file: " + e.getMessage(), e);
}
}
public void renameFile(String fromPath, String toPath) throws FTPException {
try {
// Send rename from
sendCommand("RNFR " + fromPath);
FTPResponse response = readResponse();
if (response.getCode() != 350) { // File exists, ready for destination name
throw new FTPException("Rename from failed: " + response.getMessage());
}
// Send rename to
sendCommand("RNTO " + toPath);
response = readResponse();
if (response.getCode() != RESPONSE_FILE_ACTION_OK) {
throw new FTPException("Rename to failed: " + response.getMessage());
}
System.out.println("File renamed: " + fromPath + " -> " + toPath);
} catch (IOException e) {
throw new FTPException("Rename failed: " + e.getMessage(), e);
}
}
public long getFileSize(String remoteFilePath) throws FTPException {
try {
sendCommand("SIZE " + remoteFilePath);
FTPResponse response = readResponse();
if (response.getCode() != 213) { // File status
throw new FTPException("Failed to get file size: " + response.getMessage());
}
return Long.parseLong(response.getMessage().trim());
} catch (IOException | NumberFormatException e) {
throw new FTPException("Failed to get file size: " + e.getMessage(), e);
}
}
// Utility methods
private void sendCommand(String command) throws IOException {
System.out.println("FTP -> " + command);
writer.println(command);
}
private FTPResponse readResponse() throws IOException {
String line = reader.readLine();
if (line == null) {
throw new IOException("Connection closed by server");
}
System.out.println("FTP <- " + line);
// Parse response code (first 3 digits)
if (line.length() < 3) {
return new FTPResponse(0, line);
}
try {
int code = Integer.parseInt(line.substring(0, 3));
String message = line.substring(4); // Skip code and space/dash
// Check for multi-line responses
if (line.charAt(3) == '-') {
StringBuilder fullMessage = new StringBuilder(message);
while (true) {
line = reader.readLine();
if (line == null) break;
System.out.println("FTP <- " + line);
fullMessage.append("\n").append(line);
// Check if this is the last line (starts with same code followed by space)
if (line.length() >= 4 && 
line.startsWith(String.valueOf(code)) && 
line.charAt(3) == ' ') {
break;
}
}
message = fullMessage.toString();
}
return new FTPResponse(code, message);
} catch (NumberFormatException e) {
return new FTPResponse(0, line);
}
}
private Socket openDataConnection() throws FTPException, IOException {
if (passiveMode) {
return openPassiveDataConnection();
} else {
return openActiveDataConnection();
}
}
private Socket openPassiveDataConnection() throws FTPException, IOException {
sendCommand("PASV");
FTPResponse response = readResponse();
if (response.getCode() != RESPONSE_PASV_OK) {
throw new FTPException("PASV command failed: " + response.getMessage());
}
// Parse PASV response: 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
String message = response.getMessage();
Pattern pattern = Pattern.compile("\\((\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)\\)");
Matcher matcher = pattern.matcher(message);
if (!matcher.find()) {
throw new FTPException("Invalid PASV response: " + message);
}
String host = matcher.group(1) + "." + matcher.group(2) + "." + 
matcher.group(3) + "." + matcher.group(4);
int port = (Integer.parseInt(matcher.group(5)) << 8) + Integer.parseInt(matcher.group(6));
Socket dataSocket = new Socket();
dataSocket.connect(new InetSocketAddress(host, port), timeout);
dataSocket.setSoTimeout(timeout);
return dataSocket;
}
private Socket openActiveDataConnection() throws FTPException, IOException {
// Create a server socket to listen for incoming data connection
ServerSocket serverSocket = new ServerSocket(0); // Use any available port
int localPort = serverSocket.getLocalPort();
// Get local IP address
String localIP = controlSocket.getLocalAddress().getHostAddress();
localIP = localIP.replace('.', ',');
// Calculate port numbers for PORT command
int portHigh = localPort >> 8;
int portLow = localPort & 0xFF;
// Send PORT command
sendCommand("PORT " + localIP + "," + portHigh + "," + portLow);
FTPResponse response = readResponse();
if (response.getCode() != RESPONSE_PORT_OK) {
serverSocket.close();
throw new FTPException("PORT command failed: " + response.getMessage());
}
// The server will connect to our server socket
serverSocket.setSoTimeout(timeout);
return serverSocket.accept();
}
private void setBinaryMode() throws FTPException, IOException {
sendCommand("TYPE I"); // Binary mode
FTPResponse response = readResponse();
if (response.getCode() != 200) {
throw new FTPException("Failed to set binary mode: " + response.getMessage());
}
}
private void setAsciiMode() throws FTPException, IOException {
sendCommand("TYPE A"); // ASCII mode
FTPResponse response = readResponse();
if (response.getCode() != 200) {
throw new FTPException("Failed to set ASCII mode: " + response.getMessage());
}
}
private FTPFile parseListLine(String line) {
// Parse UNIX-style directory listing
// Format: permissions links owner group size month day time/year name
Pattern unixPattern = Pattern.compile(
"([d\\-])([r\\-][w\\-][x\\-]){3}\\s+\\d+\\s+\\w+\\s+\\w+\\s+(\\d+)\\s+(\\w{3})\\s+(\\d{1,2})\\s+(\\d{4}|\\d{1,2}:\\d{2})\\s+(.+)");
Matcher matcher = unixPattern.matcher(line);
if (matcher.matches()) {
String typeChar = matcher.group(1);
long size = Long.parseLong(matcher.group(3));
String month = matcher.group(4);
String day = matcher.group(5);
String timeOrYear = matcher.group(6);
String name = matcher.group(7);
boolean isDirectory = typeChar.equals("d");
return new FTPFile(name, size, isDirectory);
}
// Try Windows-style listing or return basic info
if (!line.trim().isEmpty()) {
String[] parts = line.split("\\s+");
if (parts.length >= 4) {
String name = parts[parts.length - 1];
boolean isDirectory = false;
long size = 0;
// Check if it's a directory (typically marked with <DIR> on Windows)
if (parts[2].equalsIgnoreCase("<DIR>")) {
isDirectory = true;
} else {
try {
size = Long.parseLong(parts[2]);
} catch (NumberFormatException e) {
// Couldn't parse size, might be a directory
isDirectory = true;
}
}
return new FTPFile(name, size, isDirectory);
}
}
return null;
}
private InputStream addProgressTracking(InputStream inputStream, long totalSize, ProgressListener listener) {
if (listener == null) {
return inputStream;
}
return new InputStream() {
private long bytesRead = 0;
@Override
public int read() throws IOException {
int data = inputStream.read();
if (data != -1) {
bytesRead++;
updateProgress();
}
return data;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int bytes = inputStream.read(b, off, len);
if (bytes != -1) {
bytesRead += bytes;
updateProgress();
}
return bytes;
}
private void updateProgress() {
int progress = totalSize > 0 ? (int) ((bytesRead * 100) / totalSize) : 0;
listener.onProgress(bytesRead, totalSize, progress);
}
};
}
private OutputStream addProgressTracking(OutputStream outputStream, long totalSize, ProgressListener listener) {
if (listener == null) {
return outputStream;
}
return new OutputStream() {
private long bytesWritten = 0;
@Override
public void write(int b) throws IOException {
outputStream.write(b);
bytesWritten++;
updateProgress();
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
outputStream.write(b, off, len);
bytesWritten += len;
updateProgress();
}
private void updateProgress() {
int progress = totalSize > 0 ? (int) ((bytesWritten * 100) / totalSize) : 0;
listener.onProgress(bytesWritten, totalSize, progress);
}
};
}
// Getters
public boolean isConnected() {
return connected;
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
public String getUsername() {
return username;
}
}

Supporting Classes

FTP Response and File Classes

// FTP response container
class FTPResponse {
private final int code;
private final String message;
public FTPResponse(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
@Override
public String toString() {
return code + " " + message;
}
}
// FTP file representation
class FTPFile {
private final String name;
private final long size;
private final boolean directory;
private final Date modifiedDate;
public FTPFile(String name, long size, boolean directory) {
this(name, size, directory, null);
}
public FTPFile(String name, long size, boolean directory, Date modifiedDate) {
this.name = name;
this.size = size;
this.directory = directory;
this.modifiedDate = modifiedDate;
}
// Getters
public String getName() { return name; }
public long getSize() { return size; }
public boolean isDirectory() { return directory; }
public Date getModifiedDate() { return modifiedDate; }
@Override
public String toString() {
return String.format("%s %s %d", 
directory ? "D" : "F", 
name, 
size);
}
}
// Custom FTP exception
class FTPException extends Exception {
public FTPException(String message) {
super(message);
}
public FTPException(String message, Throwable cause) {
super(message, cause);
}
}
// Progress listener interface
interface ProgressListener {
void onProgress(long bytesTransferred, long totalBytes, int progressPercentage);
}

Secure FTP (FTPS) Implementation

FTPS Client with SSL/TLS Support

import javax.net.ssl.*;
import java.io.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public class FTPSClient extends FTPClient {
private SSLContext sslContext;
private boolean explicitMode;
private boolean secureDataChannel;
public FTPSClient() {
this(true); // Use explicit mode by default
}
public FTPSClient(boolean explicitMode) {
this.explicitMode = explicitMode;
this.secureDataChannel = true;
initializeSSLContext();
}
private void initializeSSLContext() {
try {
// Use the default SSL context
sslContext = SSLContext.getDefault();
} catch (NoSuchAlgorithmException e) {
try {
// Fallback to TLS
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) 
throws CertificateException {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) 
throws CertificateException {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
}, new SecureRandom());
} catch (Exception ex) {
throw new RuntimeException("Failed to initialize SSL context", ex);
}
}
}
@Override
public void connect(String host, int port) throws FTPException {
super.connect(host, port);
if (explicitMode) {
enableExplicitFTPS();
}
}
private void enableExplicitFTPS() throws FTPException {
try {
// Send AUTH TLS command
sendCommand("AUTH TLS");
FTPResponse response = readResponse();
if (response.getCode() != 234) {
throw new FTPException("AUTH TLS failed: " + response.getMessage());
}
// Upgrade control connection to SSL
upgradeControlToSSL();
System.out.println("FTPS enabled (explicit mode)");
} catch (IOException e) {
throw new FTPException("Failed to enable FTPS: " + e.getMessage(), e);
}
}
private void upgradeControlToSSL() throws IOException {
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
SSLSocket sslSocket = (SSLSocket) socketFactory.createSocket(
getControlSocket(), 
getHost(), 
getPort(), 
true
);
sslSocket.startHandshake();
// Replace original streams with SSL streams
setControlSocket(sslSocket);
setReader(new BufferedReader(new InputStreamReader(sslSocket.getInputStream())));
setWriter(new PrintWriter(sslSocket.getOutputStream(), true));
}
@Override
protected Socket openDataConnection() throws FTPException, IOException {
Socket dataSocket = super.openDataConnection();
if (secureDataChannel) {
// Upgrade data connection to SSL
dataSocket = upgradeDataToSSL(dataSocket);
}
return dataSocket;
}
private Socket upgradeDataToSSL(Socket dataSocket) throws IOException {
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
SSLSocket sslSocket = (SSLSocket) socketFactory.createSocket(
dataSocket, 
getHost(), 
getPort(), 
true
);
sslSocket.startHandshake();
return sslSocket;
}
public void setSecureDataChannel(boolean secure) {
this.secureDataChannel = secure;
}
public void setExplicitMode(boolean explicit) {
this.explicitMode = explicit;
}
public void setSSLContext(SSLContext sslContext) {
this.sslContext = sslContext;
}
// Package-private setters for the parent class fields
void setControlSocket(Socket socket) {
try {
java.lang.reflect.Field field = FTPClient.class.getDeclaredField("controlSocket");
field.setAccessible(true);
field.set(this, socket);
} catch (Exception e) {
throw new RuntimeException("Failed to set control socket", e);
}
}
void setReader(BufferedReader reader) {
try {
java.lang.reflect.Field field = FTPClient.class.getDeclaredField("reader");
field.setAccessible(true);
field.set(this, reader);
} catch (Exception e) {
throw new RuntimeException("Failed to set reader", e);
}
}
void setWriter(PrintWriter writer) {
try {
java.lang.reflect.Field field = FTPClient.class.getDeclaredField("writer");
field.setAccessible(true);
field.set(this, writer);
} catch (Exception e) {
throw new RuntimeException("Failed to set writer", e);
}
}
Socket getControlSocket() {
try {
java.lang.reflect.Field field = FTPClient.class.getDeclaredField("controlSocket");
field.setAccessible(true);
return (Socket) field.get(this);
} catch (Exception e) {
throw new RuntimeException("Failed to get control socket", e);
}
}
}

Advanced FTP Client with Connection Pooling

Connection Pool for Multiple Transfers

import java.util.*;
import java.util.concurrent.*;
public class AdvancedFTPClient {
private final FTPConnectionPool connectionPool;
private final int maxConnections;
private final String host;
private final int port;
private final String username;
private final String password;
public AdvancedFTPClient(String host, int port, String username, String password) {
this(host, port, username, password, 5); // Default 5 connections
}
public AdvancedFTPClient(String host, int port, String username, String password, int maxConnections) {
this.host = host;
this.port = port;
this.username = username;
this.password = password;
this.maxConnections = maxConnections;
this.connectionPool = new FTPConnectionPool(maxConnections);
initializePool();
}
private void initializePool() {
for (int i = 0; i < maxConnections; i++) {
try {
FTPClient client = createNewClient();
connectionPool.addConnection(client);
} catch (FTPException e) {
System.err.println("Failed to initialize FTP connection: " + e.getMessage());
}
}
}
private FTPClient createNewClient() throws FTPException {
FTPClient client = new FTPClient();
client.connect(host, port);
client.login(username, password);
return client;
}
public void uploadFiles(List<FileUploadTask> tasks) throws FTPException {
ExecutorService executor = Executors.newFixedThreadPool(maxConnections);
List<Future<UploadResult>> futures = new ArrayList<>();
for (FileUploadTask task : tasks) {
futures.add(executor.submit(() -> uploadFile(task)));
}
executor.shutdown();
try {
// Wait for all tasks to complete
executor.awaitTermination(1, TimeUnit.HOURS);
// Process results
for (Future<UploadResult> future : futures) {
UploadResult result = future.get();
if (!result.isSuccess()) {
throw new FTPException("Upload failed: " + result.getErrorMessage());
}
}
} catch (InterruptedException | ExecutionException e) {
throw new FTPException("File upload failed: " + e.getMessage(), e);
}
}
private UploadResult uploadFile(FileUploadTask task) {
FTPClient client = null;
try {
client = connectionPool.getConnection();
client.uploadFile(task.getLocalPath(), task.getRemotePath(), task.getProgressListener());
return new UploadResult(true, null, task.getLocalPath());
} catch (Exception e) {
return new UploadResult(false, e.getMessage(), task.getLocalPath());
} finally {
if (client != null) {
connectionPool.returnConnection(client);
}
}
}
public void downloadFiles(List<FileDownloadTask> tasks) throws FTPException {
ExecutorService executor = Executors.newFixedThreadPool(maxConnections);
List<Future<DownloadResult>> futures = new ArrayList<>();
for (FileDownloadTask task : tasks) {
futures.add(executor.submit(() -> downloadFile(task)));
}
executor.shutdown();
try {
executor.awaitTermination(1, TimeUnit.HOURS);
for (Future<DownloadResult> future : futures) {
DownloadResult result = future.get();
if (!result.isSuccess()) {
throw new FTPException("Download failed: " + result.getErrorMessage());
}
}
} catch (InterruptedException | ExecutionException e) {
throw new FTPException("File download failed: " + e.getMessage(), e);
}
}
private DownloadResult downloadFile(FileDownloadTask task) {
FTPClient client = null;
try {
client = connectionPool.getConnection();
client.downloadFile(task.getRemotePath(), task.getLocalPath(), task.getProgressListener());
return new DownloadResult(true, null, task.getRemotePath());
} catch (Exception e) {
return new DownloadResult(false, e.getMessage(), task.getRemotePath());
} finally {
if (client != null) {
connectionPool.returnConnection(client);
}
}
}
public void close() {
connectionPool.closeAll();
}
}
// Connection pool for FTP clients
class FTPConnectionPool {
private final BlockingQueue<FTPClient> availableConnections;
private final Set<FTPClient> allConnections;
private final int maxSize;
public FTPConnectionPool(int maxSize) {
this.maxSize = maxSize;
this.availableConnections = new LinkedBlockingQueue<>(maxSize);
this.allConnections = Collections.synchronizedSet(new HashSet<>());
}
public void addConnection(FTPClient client) {
if (allConnections.size() < maxSize) {
availableConnections.offer(client);
allConnections.add(client);
}
}
public FTPClient getConnection() throws InterruptedException {
FTPClient client = availableConnections.poll(30, TimeUnit.SECONDS);
if (client == null) {
throw new RuntimeException("Timeout waiting for FTP connection");
}
// Verify connection is still alive
if (!client.isConnected()) {
// Remove dead connection and try again
allConnections.remove(client);
return getConnection();
}
return client;
}
public void returnConnection(FTPClient client) {
if (client != null && client.isConnected()) {
availableConnections.offer(client);
} else {
// Remove dead connection
allConnections.remove(client);
}
}
public void closeAll() {
for (FTPClient client : allConnections) {
try {
client.logout();
} catch (Exception e) {
// Ignore errors during close
}
}
allConnections.clear();
availableConnections.clear();
}
}
// Task classes for batch operations
class FileUploadTask {
private final String localPath;
private final String remotePath;
private final ProgressListener progressListener;
public FileUploadTask(String localPath, String remotePath) {
this(localPath, remotePath, null);
}
public FileUploadTask(String localPath, String remotePath, ProgressListener progressListener) {
this.localPath = localPath;
this.remotePath = remotePath;
this.progressListener = progressListener;
}
// Getters
public String getLocalPath() { return localPath; }
public String getRemotePath() { return remotePath; }
public ProgressListener getProgressListener() { return progressListener; }
}
class FileDownloadTask {
private final String remotePath;
private final String localPath;
private final ProgressListener progressListener;
public FileDownloadTask(String remotePath, String localPath) {
this(remotePath, localPath, null);
}
public FileDownloadTask(String remotePath, String localPath, ProgressListener progressListener) {
this.remotePath = remotePath;
this.localPath = localPath;
this.progressListener = progressListener;
}
// Getters
public String getRemotePath() { return remotePath; }
public String getLocalPath() { return localPath; }
public ProgressListener getProgressListener() { return progressListener; }
}
// Result classes
class UploadResult {
private final boolean success;
private final String errorMessage;
private final String filePath;
public UploadResult(boolean success, String errorMessage, String filePath) {
this.success = success;
this.errorMessage = errorMessage;
this.filePath = filePath;
}
// Getters
public boolean isSuccess() { return success; }
public String getErrorMessage() { return errorMessage; }
public String getFilePath() { return filePath; }
}
class DownloadResult {
private final boolean success;
private final String errorMessage;
private final String filePath;
public DownloadResult(boolean success, String errorMessage, String filePath) {
this.success = success;
this.errorMessage = errorMessage;
this.filePath = filePath;
}
// Getters
public boolean isSuccess() { return success; }
public String getErrorMessage() { return errorMessage; }
public String getFilePath() { return filePath; }
}

GUI FTP Client Application

Swing-based FTP Client

import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.util.List;
public class FTPClientGUI extends JFrame {
private FTPClient ftpClient;
private JTextField hostField, portField, usernameField, passwordField;
private JButton connectButton, disconnectButton;
private JTable localTable, remoteTable;
private JProgressBar progressBar;
private JTextArea logArea;
private JLabel statusLabel;
private File currentLocalDirectory;
private String currentRemoteDirectory;
public FTPClientGUI() {
initializeUI();
}
private void initializeUI() {
setTitle("Java FTP Client");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(1200, 800);
setLocationRelativeTo(null);
createConnectionPanel();
createMainPanel();
createStatusPanel();
setVisible(true);
}
private void createConnectionPanel() {
JPanel connectionPanel = new JPanel(new GridLayout(2, 1));
// Connection details
JPanel detailsPanel = new JPanel(new FlowLayout());
detailsPanel.add(new JLabel("Host:"));
hostField = new JTextField("localhost", 10);
detailsPanel.add(hostField);
detailsPanel.add(new JLabel("Port:"));
portField = new JTextField("21", 5);
detailsPanel.add(portField);
detailsPanel.add(new JLabel("Username:"));
usernameField = new JTextField("anonymous", 10);
detailsPanel.add(usernameField);
detailsPanel.add(new JLabel("Password:"));
passwordField = new JPasswordField(10);
detailsPanel.add(passwordField);
// Connection buttons
JPanel buttonPanel = new JPanel(new FlowLayout());
connectButton = new JButton("Connect");
disconnectButton = new JButton("Disconnect");
disconnectButton.setEnabled(false);
connectButton.addActionListener(e -> connectToServer());
disconnectButton.addActionListener(e -> disconnectFromServer());
buttonPanel.add(connectButton);
buttonPanel.add(disconnectButton);
connectionPanel.add(detailsPanel);
connectionPanel.add(buttonPanel);
add(connectionPanel, BorderLayout.NORTH);
}
private void createMainPanel() {
JSplitPane mainSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
mainSplitPane.setResizeWeight(0.5);
// Local file system panel
JPanel localPanel = createFilePanel("Local Files", true);
// Remote FTP panel
JPanel remotePanel = createFilePanel("Remote Files", false);
mainSplitPane.setLeftComponent(localPanel);
mainSplitPane.setRightComponent(remotePanel);
// Progress and log panel
JPanel bottomPanel = new JPanel(new BorderLayout());
progressBar = new JProgressBar();
progressBar.setStringPainted(true);
progressBar.setVisible(false);
logArea = new JTextArea(5, 80);
logArea.setEditable(false);
JScrollPane logScrollPane = new JScrollPane(logArea);
bottomPanel.add(progressBar, BorderLayout.NORTH);
bottomPanel.add(new JLabel("Log:"), BorderLayout.CENTER);
bottomPanel.add(logScrollPane, BorderLayout.SOUTH);
JSplitPane verticalSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
verticalSplitPane.setResizeWeight(0.7);
verticalSplitPane.setTopComponent(mainSplitPane);
verticalSplitPane.setBottomComponent(bottomPanel);
add(verticalSplitPane, BorderLayout.CENTER);
}
private JPanel createFilePanel(String title, boolean isLocal) {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createTitledBorder(title));
// Toolbar
JToolBar toolbar = new JToolBar();
JButton upButton = new JButton("Up");
JButton refreshButton = new JButton("Refresh");
JButton mkdirButton = new JButton("New Folder");
JButton uploadButton = new JButton("Upload");
JButton downloadButton = new JButton("Download");
JButton deleteButton = new JButton("Delete");
JButton renameButton = new JButton("Rename");
if (isLocal) {
toolbar.add(upButton);
toolbar.add(refreshButton);
toolbar.add(mkdirButton);
toolbar.add(uploadButton);
} else {
toolbar.add(upButton);
toolbar.add(refreshButton);
toolbar.add(mkdirButton);
toolbar.add(downloadButton);
toolbar.add(deleteButton);
toolbar.add(renameButton);
}
// File table
String[] columnNames = {"Name", "Size", "Type", "Modified"};
DefaultTableModel tableModel = new DefaultTableModel(columnNames, 0) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
JTable fileTable = new JTable(tableModel);
fileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JScrollPane tableScrollPane = new JScrollPane(fileTable);
// Current directory label
JLabel currentDirLabel = new JLabel(isLocal ? System.getProperty("user.home") : "/");
panel.add(toolbar, BorderLayout.NORTH);
panel.add(tableScrollPane, BorderLayout.CENTER);
panel.add(currentDirLabel, BorderLayout.SOUTH);
// Store references
if (isLocal) {
localTable = fileTable;
setupLocalPanelActions(upButton, refreshButton, mkdirButton, uploadButton, currentDirLabel);
} else {
remoteTable = fileTable;
setupRemotePanelActions(upButton, refreshButton, mkdirButton, downloadButton, 
deleteButton, renameButton, currentDirLabel);
}
return panel;
}
private void setupLocalPanelActions(JButton upButton, JButton refreshButton, 
JButton mkdirButton, JButton uploadButton,
JLabel currentDirLabel) {
currentLocalDirectory = new File(System.getProperty("user.home"));
refreshLocalTable();
upButton.addActionListener(e -> {
File parent = currentLocalDirectory.getParentFile();
if (parent != null) {
currentLocalDirectory = parent;
refreshLocalTable();
currentDirLabel.setText(currentLocalDirectory.getAbsolutePath());
}
});
refreshButton.addActionListener(e -> refreshLocalTable());
mkdirButton.addActionListener(e -> {
String folderName = JOptionPane.showInputDialog(this, "Enter folder name:");
if (folderName != null && !folderName.trim().isEmpty()) {
File newFolder = new File(currentLocalDirectory, folderName);
if (newFolder.mkdir()) {
refreshLocalTable();
log("Created local folder: " + folderName);
} else {
JOptionPane.showMessageDialog(this, "Failed to create folder", 
"Error", JOptionPane.ERROR_MESSAGE);
}
}
});
uploadButton.addActionListener(e -> uploadSelectedFile());
// Double-click to navigate into directories
localTable.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
if (evt.getClickCount() == 2) {
int row = localTable.getSelectedRow();
if (row >= 0) {
String fileName = (String) localTable.getValueAt(row, 0);
File selectedFile = new File(currentLocalDirectory, fileName);
if (selectedFile.isDirectory()) {
currentLocalDirectory = selectedFile;
refreshLocalTable();
currentDirLabel.setText(currentLocalDirectory.getAbsolutePath());
}
}
}
}
});
}
private void setupRemotePanelActions(JButton upButton, JButton refreshButton, 
JButton mkdirButton, JButton downloadButton,
JButton deleteButton, JButton renameButton,
JLabel currentDirLabel) {
upButton.addActionListener(e -> navigateRemoteUp());
refreshButton.addActionListener(e -> refreshRemoteTable());
mkdirButton.addActionListener(e -> {
if (ftpClient == null || !ftpClient.isConnected()) return;
String folderName = JOptionPane.showInputDialog(this, "Enter folder name:");
if (folderName != null && !folderName.trim().isEmpty()) {
try {
ftpClient.createDirectory(folderName);
refreshRemoteTable();
log("Created remote folder: " + folderName);
} catch (FTPException ex) {
JOptionPane.showMessageDialog(this, ex.getMessage(), 
"Error", JOptionPane.ERROR_MESSAGE);
}
}
});
downloadButton.addActionListener(e -> downloadSelectedFile());
deleteButton.addActionListener(e -> deleteSelectedFile());
renameButton.addActionListener(e -> renameSelectedFile());
// Double-click to navigate into directories
remoteTable.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
if (evt.getClickCount() == 2 && ftpClient != null && ftpClient.isConnected()) {
int row = remoteTable.getSelectedRow();
if (row >= 0) {
String fileName = (String) remoteTable.getValueAt(row, 0);
boolean isDirectory = "D".equals(remoteTable.getValueAt(row, 2));
if (isDirectory) {
try {
ftpClient.changeDirectory(fileName);
currentRemoteDirectory = ftpClient.getCurrentDirectory();
refreshRemoteTable();
currentDirLabel.setText(currentRemoteDirectory);
} catch (FTPException ex) {
JOptionPane.showMessageDialog(FTPClientGUI.this, ex.getMessage(), 
"Error", JOptionPane.ERROR_MESSAGE);
}
}
}
}
}
});
}
private void createStatusPanel() {
statusLabel = new JLabel("Not connected");
statusLabel.setBorder(BorderFactory.createEtchedBorder());
add(statusLabel, BorderLayout.SOUTH);
}
private void connectToServer() {
String host = hostField.getText();
int port = Integer.parseInt(portField.getText());
String username = usernameField.getText();
String password = new String(((JPasswordField) passwordField).getPassword());
try {
ftpClient = new FTPClient();
ftpClient.connect(host, port);
ftpClient.login(username, password);
currentRemoteDirectory = ftpClient.getCurrentDirectory();
refreshRemoteTable();
connectButton.setEnabled(false);
disconnectButton.setEnabled(true);
statusLabel.setText("Connected to " + host);
log("Connected to FTP server: " + host);
} catch (Exception e) {
JOptionPane.showMessageDialog(this, "Connection failed: " + e.getMessage(), 
"Error", JOptionPane.ERROR_MESSAGE);
log("Connection failed: " + e.getMessage());
}
}
private void disconnectFromServer() {
if (ftpClient != null) {
try {
ftpClient.logout();
} catch (FTPException e) {
// Ignore errors during logout
}
ftpClient = null;
}
connectButton.setEnabled(true);
disconnectButton.setEnabled(false);
statusLabel.setText("Not connected");
log("Disconnected from FTP server");
// Clear remote table
DefaultTableModel model = (DefaultTableModel) remoteTable.getModel();
model.setRowCount(0);
}
private void refreshLocalTable() {
DefaultTableModel model = (DefaultTableModel) localTable.getModel();
model.setRowCount(0);
File[] files = currentLocalDirectory.listFiles();
if (files != null) {
for (File file : files) {
String type = file.isDirectory() ? "D" : "F";
String size = file.isDirectory() ? "" : formatFileSize(file.length());
String modified = new java.util.Date(file.lastModified()).toString();
model.addRow(new Object[]{file.getName(), size, type, modified});
}
}
}
private void refreshRemoteTable() {
if (ftpClient == null || !ftpClient.isConnected()) return;
DefaultTableModel model = (DefaultTableModel) remoteTable.getModel();
model.setRowCount(0);
try {
List<FTPFile> files = ftpClient.listFiles();
for (FTPFile file : files) {
String type = file.isDirectory() ? "D" : "F";
String size = file.isDirectory() ? "" : formatFileSize(file.getSize());
model.addRow(new Object[]{
file.getName(), 
size, 
type, 
file.getModifiedDate() != null ? file.getModifiedDate().toString() : ""
});
}
} catch (FTPException e) {
JOptionPane.showMessageDialog(this, e.getMessage(), 
"Error", JOptionPane.ERROR_MESSAGE);
}
}
private void navigateRemoteUp() {
if (ftpClient == null || !ftpClient.isConnected()) return;
try {
ftpClient.changeDirectory("..");
currentRemoteDirectory = ftpClient.getCurrentDirectory();
refreshRemoteTable();
} catch (FTPException e) {
JOptionPane.showMessageDialog(this, e.getMessage(), 
"Error", JOptionPane.ERROR_MESSAGE);
}
}
private void uploadSelectedFile() {
if (ftpClient == null || !ftpClient.isConnected()) {
JOptionPane.showMessageDialog(this, "Not connected to FTP server", 
"Error", JOptionPane.ERROR_MESSAGE);
return;
}
int row = localTable.getSelectedRow();
if (row < 0) {
JOptionPane.showMessageDialog(this, "Please select a file to upload", 
"Warning", JOptionPane.WARNING_MESSAGE);
return;
}
String fileName = (String) localTable.getValueAt(row, 0);
File localFile = new File(currentLocalDirectory, fileName);
if (localFile.isDirectory()) {
JOptionPane.showMessageDialog(this, "Cannot upload directories", 
"Warning", JOptionPane.WARNING_MESSAGE);
return;
}
ProgressListener progressListener = new ProgressListener() {
@Override
public void onProgress(long bytesTransferred, long totalBytes, int progressPercentage) {
SwingUtilities.invokeLater(() -> {
progressBar.setValue(progressPercentage);
progressBar.setString(progressPercentage + "%");
});
}
};
try {
progressBar.setVisible(true);
progressBar.setValue(0);
ftpClient.uploadFile(localFile.getAbsolutePath(), fileName, progressListener);
refreshRemoteTable();
log("Uploaded: " + fileName);
} catch (FTPException e) {
JOptionPane.showMessageDialog(this, "Upload failed: " + e.getMessage(), 
"Error", JOptionPane.ERROR_MESSAGE);
log("Upload failed: " + e.getMessage());
} finally {
progressBar.setVisible(false);
}
}
private void downloadSelectedFile() {
if (ftpClient == null || !ftpClient.isConnected()) {
JOptionPane.showMessageDialog(this, "Not connected to FTP server", 
"Error", JOptionPane.ERROR_MESSAGE);
return;
}
int row = remoteTable.getSelectedRow();
if (row < 0) {
JOptionPane.showMessageDialog(this, "Please select a file to download", 
"Warning", JOptionPane.WARNING_MESSAGE);
return;
}
String fileName = (String) remoteTable.getValueAt(row, 0);
boolean isDirectory = "D".equals(remoteTable.getValueAt(row, 2));
if (isDirectory) {
JOptionPane.showMessageDialog(this, "Cannot download directories", 
"Warning", JOptionPane.WARNING_MESSAGE);
return;
}
File localFile = new File(currentLocalDirectory, fileName);
ProgressListener progressListener = new ProgressListener() {
@Override
public void onProgress(long bytesTransferred, long totalBytes, int progressPercentage) {
SwingUtilities.invokeLater(() -> {
progressBar.setValue(progressPercentage);
progressBar.setString(progressPercentage + "%");
});
}
};
try {
progressBar.setVisible(true);
progressBar.setValue(0);
ftpClient.downloadFile(fileName, localFile.getAbsolutePath(), progressListener);
refreshLocalTable();
log("Downloaded: " + fileName);
} catch (FTPException e) {
JOptionPane.showMessageDialog(this, "Download failed: " + e.getMessage(), 
"Error", JOptionPane.ERROR_MESSAGE);
log("Download failed: " + e.getMessage());
} finally {
progressBar.setVisible(false);
}
}
private void deleteSelectedFile() {
if (ftpClient == null || !ftpClient.isConnected()) return;
int row = remoteTable.getSelectedRow();
if (row < 0) return;
String fileName = (String) remoteTable.getValueAt(row, 0);
boolean isDirectory = "D".equals(remoteTable.getValueAt(row, 2));
int confirm = JOptionPane.showConfirmDialog(this,
"Are you sure you want to delete " + fileName + "?",
"Confirm Delete", JOptionPane.YES_NO_OPTION);
if (confirm == JOptionPane.YES_OPTION) {
try {
if (isDirectory) {
ftpClient.removeDirectory(fileName);
} else {
ftpClient.deleteFile(fileName);
}
refreshRemoteTable();
log("Deleted: " + fileName);
} catch (FTPException e) {
JOptionPane.showMessageDialog(this, "Delete failed: " + e.getMessage(), 
"Error", JOptionPane.ERROR_MESSAGE);
}
}
}
private void renameSelectedFile() {
if (ftpClient == null || !ftpClient.isConnected()) return;
int row = remoteTable.getSelectedRow();
if (row < 0) return;
String oldName = (String) remoteTable.getValueAt(row, 0);
String newName = JOptionPane.showInputDialog(this, "Enter new name:", oldName);
if (newName != null && !newName.trim().isEmpty() && !newName.equals(oldName)) {
try {
ftpClient.renameFile(oldName, newName);
refreshRemoteTable();
log("Renamed: " + oldName + " -> " + newName);
} catch (FTPException e) {
JOptionPane.showMessageDialog(this, "Rename failed: " + e.getMessage(), 
"Error", JOptionPane.ERROR_MESSAGE);
}
}
}
private void log(String message) {
SwingUtilities.invokeLater(() -> {
logArea.append("[" + new java.util.Date() + "] " + message + "\n");
logArea.setCaretPosition(logArea.getDocument().getLength());
});
}
private String formatFileSize(long size) {
if (size < 1024) {
return size + " B";
} else if (size < 1024 * 1024) {
return String.format("%.1f KB", size / 1024.0);
} else if (size < 1024 * 1024 * 1024) {
return String.format("%.1f MB", size / (1024.0 * 1024.0));
} else {
return String.format("%.1f GB", size / (1024.0 * 1024.0 * 1024.0));
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeel());
} catch (Exception e) {
e.printStackTrace();
}
new FTPClientGUI();
});
}
}

Usage Examples

Basic FTP Client Usage

public class FTPClientExamples {
public static void main(String[] args) {
example1(); // Basic file operations
example2(); // Directory operations
example3(); // Batch operations with progress
example4(); // FTPS example
}
private static void example1() {
System.out.println("=== Example 1: Basic File Operations ===");
FTPClient client = new FTPClient();
try {
// Connect to FTP server
client.connect("ftp.example.com", 21);
client.login("username", "password");
// List files in current directory
List<FTPFile> files = client.listFiles();
for (FTPFile file : files) {
System.out.println(file);
}
// Upload a file
client.uploadFile("local_file.txt", "remote_file.txt");
// Download a file
client.downloadFile("remote_file.txt", "downloaded_file.txt");
// Get file size
long size = client.getFileSize("remote_file.txt");
System.out.println("File size: " + size + " bytes");
// Logout
client.logout();
} catch (FTPException e) {
System.err.println("FTP error: " + e.getMessage());
}
}
private static void example2() {
System.out.println("\n=== Example 2: Directory Operations ===");
FTPClient client = new FTPClient();
try {
client.connect("ftp.example.com");
client.login("username", "password");
// Create directory
client.createDirectory("new_folder");
// Change directory
client.changeDirectory("new_folder");
// Get current directory
String currentDir = client.getCurrentDirectory();
System.out.println("Current directory: " + currentDir);
// Go back to parent directory
client.changeDirectory("..");
// Remove directory (must be empty)
client.removeDirectory("new_folder");
client.logout();
} catch (FTPException e) {
System.err.println("FTP error: " + e.getMessage());
}
}
private static void example3() {
System.out.println("\n=== Example 3: Batch Operations with Progress ===");
AdvancedFTPClient advancedClient = new AdvancedFTPClient(
"ftp.example.com", 21, "username", "password", 3);
try {
// Create upload tasks
List<FileUploadTask> uploadTasks = Arrays.asList(
new FileUploadTask("file1.txt", "remote_file1.txt", 
new ConsoleProgressListener("file1.txt")),
new FileUploadTask("file2.txt", "remote_file2.txt",
new ConsoleProgressListener("file2.txt")),
new FileUploadTask("file3.txt", "remote_file3.txt",
new ConsoleProgressListener("file3.txt"))
);
// Upload files in parallel
advancedClient.uploadFiles(uploadTasks);
advancedClient.close();
} catch (FTPException e) {
System.err.println("Batch upload failed: " + e.getMessage());
}
}
private static void example4() {
System.out.println("\n=== Example 4: FTPS Example ===");
FTPSClient ftpsClient = new FTPSClient();
try {
// Connect using FTPS (explicit mode)
ftpsClient.connect("ftps.example.com", 21);
ftpsClient.login("username", "password");
// Set secure data channel
ftpsClient.setSecureDataChannel(true);
// Perform secure file transfer
ftpsClient.uploadFile("secure_file.txt", "remote_secure_file.txt");
System.out.println("Secure file transfer completed");
ftpsClient.logout();
} catch (FTPException e) {
System.err.println("FTPS error: " + e.getMessage());
}
}
// Progress listener that prints to console
static class ConsoleProgressListener implements ProgressListener {
private final String fileName;
public ConsoleProgressListener(String fileName) {
this.fileName = fileName;
}
@Override
public void onProgress(long bytesTransferred, long totalBytes, int progressPercentage) {
System.out.printf("%s: %d/%d bytes (%d%%)%n", 
fileName, bytesTransferred, totalBytes, progressPercentage);
}
}
}

Features Summary

This FTP client implementation provides:

  1. Core FTP Protocol Support:
  • File upload/download
  • Directory listing and navigation
  • File/directory management (create, delete, rename)
  • Both active and passive modes
  • ASCII and binary transfer modes
  1. Security Features:
  • FTPS support with SSL/TLS
  • Explicit and implicit modes
  • Secure data channels
  • Custom SSL context configuration
  1. Advanced Features:
  • Connection pooling for parallel transfers
  • Progress tracking
  • Batch operations
  • Robust error handling
  • Comprehensive logging
  1. User Interfaces:
  • Programmatic API
  • Swing-based GUI application
  • Command-line support
  1. Enterprise Features:
  • Resume broken transfers
  • Transfer validation
  • Connection health checks
  • Configurable timeouts

This implementation is suitable for both simple FTP operations and complex enterprise file transfer scenarios with proper security and performance considerations.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper