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:
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:
Then the test program would produce the following output:
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.