It’s not difficult to write Java code for downloading a single file from a FTP server. However it would be quite complex if we want to download a complete directory because a directory differs greatly from a file, as it may contain sub files and sub directories which can be nested in many levels. In this article, we present a solution for downloading a whole directory from the FTP server, which is tested and proved as working very well.

The solution is based on these two tutorials:

For the sake of reusability, we implement the solution as a utility class looks like this:

public class FTPUtil {

	public static void downloadDirectory(FTPClient ftpClient, String parentDir,
			String currentDir, String saveDir) throws IOException {

		// code to download a directory...

	}

	public static boolean downloadSingleFile(FTPClient ftpClient,
			String remoteFilePath, String savePath) throws IOException {

		// code to download a file...

	}
}

Here the downloadSingleFile() method is used downloadDirectory() method and can be also used independently to download just a file. Following is the implementation of the downloadSingleFile() method: 

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

This method is pretty simple and the Javadoc explains its signature quite well.

And here is the important part - the implementation of the downloadDirectory() method:

/**
 * 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);

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

This method iterates over all files and sub directories of the current directory in the following manner:

    • If the current item is a file, call the method downloadSingleFile() to download that file.
    • If the current item is a directory, create that directory in the local computer and call the downloadDirectory() method itself. Repeat the same steps for the sub directory, and sub directory of sub directory, and so on (recursively).

The important point in this method is to re-create directory structure of the remote directory on the local computer correctly. The following example illustrates how to use this method:

FTPClient ftpClient = new FTPClient();
// connect and login...

// directory on the server to be downloaded
String remoteDirPath = "/MyPhotos";

// directory where the files will be saved
String saveDirPath = "D:/Download";

// call the utility method
FTPUtil.downloadDirectory(ftpClient, remoteDirPath, "", saveDirPath);

Notice that we always pass an empty String (“”) for the parameter currentDir when calling this method. Only the recursive calls will pass specific values for this parameter.

Now we create a test program as follows:

import java.io.IOException;

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

public class FTPDownloadDirectoryTest {

	public static void main(String[] args) {
		String server = "www.codejava.net";
		int port = 21;
		String user = "username";
		String pass = "password";

		FTPClient ftpClient = new 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();

			System.out.println("Connected");

			String remoteDirPath = "/Test";
			String saveDirPath = "E:/Test/Download/FTP";

			FTPUtil.downloadDirectory(ftpClient, remoteDirPath, "", saveDirPath);

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

			System.out.println("Disconnected");
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}
}

Compile the utility class:

javac -cp commons-net-VERSION.jar FTPUtil.java

Compile the test program:

javac -cp commons-net-VERSION.jar;. FTPDownloadDirectoryTest.java

Run the program:

java -cp commons-net-VERSION.jar;. FTPDownloadDirectoryTest

Assuming that we want to download the directory /Test on the server which has following structure:

directory structure to download

Then the test program would produce the following output:

output of program to download a whole directory

NOTE:You can download the latest distribution of the Apache Commons Net library here.

If you want to download only directory structure, see the article: Download only structure of a directory from FTP server.

 

Related Java FTP Tutorials:

 

Other Java FTP Tutorials:


About the Author:

is certified Java programmer (SCJP and SCWCD). He began programming with Java back in the days of Java 1.4 and has been passionate about it ever since. You can connect with him on Facebook and watch his Java videos on YouTube.



Attachments:
Download this file (FTPDownloadDirectoryTest.java)FTPDownloadDirectoryTest.java[Test program]0.9 kB
Download this file (FTPUtil.java)FTPUtil.java[FTP Download Utility class]3 kB

Add comment

   


Comments 

#32Manikanta2021-11-18 00:25
I have requirement to generate the zip file in users "Download" folder without using HTTPServletResponse, I have used System.getProperty("user.home") and creating the zip file ,it was working fine in my local but when it deployed in Tomcat server the files are trying to download it into Tomcat servers "Download" folder instead of users "Download" folder.

Is there any option to download the files to users "Download" folder in Java ?If so could you please share me with sample code.
Quote
#31Newbie2020-03-31 04:19
Quoting Nam:
Hi Newbie,
So you need to calculate total size of all files in the directory before downloading. And update the code logic accordingly (you have to think and do some maths).

Ok let me try. Thank you.
Quote
#30Nam2020-03-26 16:29
Hi Newbie,
So you need to calculate total size of all files in the directory before downloading. And update the code logic accordingly (you have to think and do some maths).
Quote
#29Newbie2020-03-26 10:33
Quoting Nam:
Quoting Newbie:
Hi, please instruct me how to add jprogressbar for this case ? Thanks

For your reference: codejava.net/.../...

Hi, but it's only ONE download file, it's different from this case (download multi files at the same time) so I don't know how to do ?
Quote
#28Nam2020-03-16 16:05
Quoting Newbie:
Hi, please instruct me how to add jprogressbar for this case ? Thanks

For your reference: codejava.net/.../...
Quote