java实现FTP上传(文件)、下载(文件、文件夹、文件夹递归)、删除(文件、文件夹递归)

提示:必须先保证有在FTP上上传、下载、删除的权限!

 

本文结构 ---- 先给出测试样例(图片版),再给出工具类代码(文字版)!

 

上传测试

注意:.uploadFile(String remoteDir, String remoteFileName, File file)中,remoteDir统一写(以“/”分割的)绝对路径;
        
remoteDir可以是不存在的目录(会自动创建);更多详情见代码注释!

运行主函数:可看到上传成功(ie浏览器打开ftp://10.2.6.16:22/test/):

多次上传,得到现有目录(ie浏览器打开ftp://10.2.6.16:22/test/):

可以看见:/test/下有a、b、c这三个目录(每个目录下都有各自的文件);还有SQL语句.txt
               
justry_deng.htmlJustryDeng.shuaige这三个文件了。

由此可见:上传成功!

 

单文件下载测试

注:.downloadFile(String remoteDirOrRemoteFile, String localDir)中,remoteDirOrRemoteFile统一
     写(以“/”分割的)绝对路径
;localDir可以是不存在的目录(会自动创建);更多详情见代码注释!

注:如果remoteDirOrRemoteFile不存在,那么不会下载下来任何东西。

运行主函数,可看到下载成功:

注:下载下来的文件的内容也是正确无乱码的。

:.recursiveDownloadFile(String remoteDirOrRemoteFile, String localDir)方法也支持单文件下载

由此可见:文件下载成功!

 

指定目录下所有文件下载(不包括:下载该目录下的文件夹及其内部内容)测试

注:.downloadFile(String remoteDirOrRemoteFile, String localDir)中,remoteDirOrRemoteFile统一
     写(以“/”分割的)绝对路径
;localDir可以是不存在的目录(会自动创建);更多详情见代码注释!

运行主函数,可看到下载成功:

注:可以看到,不仅把FTP 的/test/目录下的文件下载下来了,并没有下载/test/目录下的a、b、c文件夹。

由此可见:指定目录下所有文件下载成功!

 

指定目录下所有文件下载(还包括:下载该目录下的文件夹及其内部内容)测试

注:.recursiveDownloadFile(String remoteDirOrRemoteFile, String localDir)中,remoteDirOrRemoteFile统一
     写(以“/”分割的)绝对路径
;localDir可以是不存在的目录(会自动创建);更多详情见代码注释!

运行主函数,可看到下载成功:

注:可以看到,不仅把FTP 的/test/目录下的有SQL语句.txtjustry_deng.htmlJustryDeng.shuaige这三个文件
     下载下来了,还把/test/目录下的abc文件夹(以及每个文件夹里面的文件、子文件夹)都下载下来了。

由此可见:指定目录下所有文件、文件夹(递归)下载成功!

 

 

单文件删除测试

注:.deleteBlankDirOrFile(String deletedBlankDirOrFile)中,deletedBlankDirOrFile统一写(以“/”分割的)绝对路径,
     可以是一个明确的要删除的文件全路径;也可以是一个空的文件夹路径
;更多详情见代码注释!

运行主函数,可看到下载成功(ie浏览器打开ftp://10.2.6.16:22/):

注:可以看到,FTP 的/test/目录下的JustryDeng.shuaige文件已经被删除了。

由此可见:单文件删除成功!

 

 

指定目录删除(包括:删除该目录下的文件夹及其内部内容)测试

删除前,FTP是有/test/目录的,且其内容为:

调用FTP工具类删除:

运行主函数后,ie浏览器打开ftp://10.2.6.16:22/,可以看见:

FTP目录下找不到test目录了,即:删除test文件夹(即里面的内容)成功!

由此可见:文件夹(含不含内容都可以)删除成功!

 

 

>>> 进入正题

本人测试时,软硬件环境: JDK1.8、Windows、Eclipse、SpringBoot、FTP服务器(windows下的)

准备工作:在pom.xml中引入依赖

<!-- 
    Apache Commons Net library contains a collection of network utilities 
	and protocol implementations. Supported protocols include: Echo, Finger, 
	FTP, NNTP, NTP, POP3(S), SMTP(S), Telnet, Whois 
-->
<dependency>
	<groupId>commons-net</groupId>
	<artifactId>commons-net</artifactId>
	<version>3.6</version>
</dependency>

FTP工具类

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 java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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;

/**
 * FTP上传、下载、删除 工具类
 * 约定:统一使用绝对路径
 *
 * @author JustryDeng
 * @DATE 2018年9月26日 上午10:38:46
 */
public class FTPUtils {
	
    /** ftp服务器地址 */
    private String hostname;

    /** 端口号 */
    private Integer port;

    /** ftp登录账号 */
    private String username;

    /** ftp登录密码 */
    private String password;
    
    /**
     * 命令 语句 编码(控制发出去的命令的编码) 
     * 如:在删除时,发出去的指令由于此处的编码不对应的原因,乱码了;(找不到目标文件)导致删除失败
     * 如:在下载时,发出去的指令由于此处的编码不对应的原因,乱码了;(找不到目标文件)导致下载失败
     * 如:在上传时,发出去的指令由于此处的编码不对应的原因,乱码了;导致上传到FTP的文件的文件名乱码
     *                
     * 注:根据不同的(Server/Client)情况,这里灵活设置
     */
    private String sendCommandStringEncoding = "ISO-8859-1";
    
    /**
     * 下载文件,文件名encode编码
     * 
     * 注:根据不同的(Server/Client)情况,这里灵活设置
     */
    private String downfileNameEncodingParam1 = "ISO-8859-1";
    
    /**
     * 下载文件,文件名decode编码
     * 
     * 注:根据不同的(Server/Client)情况,这里灵活设置
     */
    private String downfileNameDecodingParam2 = "GBK";
    
    /**
     * 设置文件传输形式(使用FTP类静态常量赋值即可)
     * 
     * 注:根据要下载上传的文件情况,这里灵活设置
     */
    private Integer transportFileType = FTP.BINARY_FILE_TYPE;
    
    /** FTP客户端引用 */
    private FTPClient ftpClient = null;
    
    public FTPClient getFtpClient() {
		return ftpClient;
	}

	private FTPUtils(String hostname, Integer port, String username, String password) {
		super();
		this.hostname = hostname;
		this.port = port;
		this.username = username;
		this.password = password;
	}
	

	/**
	 * 设置下载时,文件名的编码
	 * 即:new String(file.getName().getBytes(param1), param2) 中的param1
	 * 注:根据不同的(Server/Client)情况,这里灵活设置
	 *
	 * @DATE 2018年9月26日 下午7:34:26
	 */
    public void setDownfileNameEncodingParam1(String downfileNameEncodingParam1) {
		this.downfileNameEncodingParam1 = downfileNameEncodingParam1;
	}

	/**
	 * 设置下载时,文件名的编码
	 * 即:new String(file.getName().getBytes(param1), param2) 中的param2
	 * 注:根据不同的(Server/Client)情况,这里灵活设置
     *
	 * @DATE 2018年9月26日 下午7:34:26
	 */
	public void setDownfileNameDecodingParam2(String downfileNameDecodingParam2) {
		this.downfileNameDecodingParam2 = downfileNameDecodingParam2;
	}
	

	/**
	 * 设置文件传输形式 -> 二进制
	 * 根据自己的时机情况,选择FTP.BINARY_FILE_TYPE或FTP.ASCII_FILE_TYPE等即可
	 * 注:根据不同的文件情况,这里灵活设置
	 *
	 * @DATE 2018年9月27日 上午9:48:51
	 */
	public void setTransportFileType(Integer transportFileType) {
		if( transportFileType != null) {
		    this.transportFileType = transportFileType;
		}
	}
	
	/** 
	 * FTP的上传、下载、删除,底层还是 发送得命令语句; 这里就设置发送的命令语句的编码
	 * 如:在删除时,发出去的指令由于此处的编码不对应的原因,乱码了;(找不到目标文件)导致删除失败
	 * 如:在下载时,发出去的指令由于此处的编码不对应的原因,乱码了;(找不到目标文件)导致下载失败
	 * 如:在上传时,发出去的指令由于此处的编码不对应的原因,乱码了;导致上传到FTP的文件的文件名乱码
	 * 
	 *  Saves the character encoding to be used by the FTP control connection.
	 *  Some FTP servers require that commands be issued in a non-ASCII
	 *  encoding like UTF-8 so that filenames with multi-byte character
	 *  representations (e.g, Big 8) can be specified.
	 */
	public void setSendCommandStringEncoding(String sendCommandStringEncoding) {
		this.sendCommandStringEncoding = sendCommandStringEncoding;
	}

	/**
     * @param hostname
     *            FTPServer ip
     * @param port
     *            FTPServer 端口
     * @return FTPUtils实例
     * @DATE 2018年9月26日 下午4:35:40
     */
    public static FTPUtils getFTPUtilsInstance(String hostname, Integer port) {
    	return getFTPUtilsInstance(hostname, port, null, null);
    }

	/**
     * @param hostname
     *            FTPServer ip
     * @param port
     *            FTPServer 端口
     * @param username
     *            用户名
     * @param password
     *            密码
     * @return FTPUtils实例
     * @DATE 2018年9月26日 下午4:39:02
     */
    public static FTPUtils getFTPUtilsInstance(String hostname, Integer port, String username, String password) {
    	return new FTPUtils(hostname, port, username, password);
    }

    /**
     * 初始化FTP服务器
     * 注:连接FTP服务器后,当前目录(即:session)默认处于根目录“/”下;
     *    所以如果一开始就是相对路径的话,那么是相对根目录的
     *
     * @throws IOException
     * @DATE 2018年9月26日 下午1:37:14
     */
    private void initFtpClient() throws IOException {
        ftpClient = new FTPClient();
        ftpClient.setControlEncoding(sendCommandStringEncoding);
        System.out.println(" FTPUtils -> connecting FTPServer -> " + this.hostname + ":" + this.port); 
        // 连接ftp服务器
        ftpClient.connect(hostname, port);
        // Returns the integer value of the reply code of the last FTP reply.
        int replyCode = ftpClient.getReplyCode(); 
        if(FTPReply.isPositiveCompletion(replyCode)){
        	System.out.println(" FTPUtils -> Connection is opened --- success !"); 
        } else {
        	System.err.println(" FTPUtils -> Connection is not open --- fail !"); 
        }
        
        // ip,端口没问题,连接成功后;如果连接FTP需要账号密码的话,还需要进行登录
        if(username != null && password != null) {
        	//登录ftp服务器
            boolean isLogined = ftpClient.login(username, password); 
            if (isLogined) {
            	System.out.println(" FTPUtils -> " + username + " login success !");
            } else {
            	System.err.println(" FTPUtils -> " + username + " login fail ! "
            			               + "Please check your Account and Password");
            }
        } else {
        	System.out.println(" FTPUtils -> login Success if "
        			                + "FTPServer settings doesn't need Account Password!");
        }
        
        // 设置文件传输形式
        ftpClient.setFileType(transportFileType);
    }

    /**
     * 上传文件至FTP
     * 注:若有同名文件,那么原文件会被覆盖
     *
     * @param remoteDir
     *            上传到指定目录(绝对路径)  如果写的相对路径,那么会默认在其前面加一个"/"
     *         统一:路径分割符 用“/”,而不用“\”;
     *         
     * @param remoteFileName
     *            上传到FTP,该文件的文件名
     * @param file
     *            要上传的本地文件
     * @return 上传结果
     * @throws IOException
     * @DATE 2018年9月26日 下午1:35:27
     */
    public boolean uploadFile(String remoteDir, String remoteFileName, File file) throws IOException{
        boolean result = false;
        InputStream inputStream = null;
        try{
            inputStream = new FileInputStream(file);
            // 初始化
            initFtpClient();
            CreateDirecroty(remoteDir);
            ftpClient.makeDirectory(remoteDir);
            ftpClient.changeWorkingDirectory(remoteDir);
            result = ftpClient.storeFile(remoteFileName, inputStream);
        }finally{
        	// 先登出、再关闭连接
        	ftpClient.logout();
            if(ftpClient.isConnected()){ 
                ftpClient.disconnect();
            } 
            if(null != inputStream){
                inputStream.close();
            } 
        }
        System.out.println(" FTPUtils -> uploadFile boolean result is ---> " + result);
        return result;
    }

    
    /**
     * 从FTP下载文件
     * 注:如果remoteDirOrRemoteFile不存在,再不会下载下来任何东西
     * 注:如果remoteDirOrRemoteFile不存在,localDir也不存在;再不会下载下来任何东西,
     *    也不会在本地创建localDir目录
     * 提示:此处代码由于把无后缀名文件也考虑在内了,所以代码略显冗余;后期有时间我会进行优化
     *
     * @param remoteDirOrRemoteFile
     *            FTP中的某一个目录(此时下载该目录下的所有文件,该目录下的文件夹不会被下载); 
     *            或  FTP中的某一个文件全路径名(此时下载该文件)
     *        统一:路径分割符 用“/”,而不用“\”;
     *        
     * @param localDir
     *            本地用于保存下载下来的文件的文件夹
     *        统一:路径分割符 用“/”,而不用“\”;
     * @return 下载了的文件个数
     * @throws IOException
     * @DATE 2018年9月26日 下午7:24:11
     */
    public int downloadFile(String remoteDirOrRemoteFile, String localDir) throws IOException{ 
        int successSum = 0; 
        int failSum = 0; 
        OutputStream os=null;
        // 这里先根据本地目录创建一个File实例,下面再根据是否有下载的文件,来判断(如果此目录不存在的话则创建此目录)
        File localFileDir = new File(localDir);
        try { 
            initFtpClient();
            // 根据remoteDirOrRemoteFile是文件还是目录,来切换changeWorkingDirectory
            if(remoteDirOrRemoteFile.lastIndexOf(".") < 0) {
	            // 切换至要下载的文件所在的目录,否者下载下来的文件大小为0
	            boolean flag = ftpClient.changeWorkingDirectory(remoteDirOrRemoteFile);
            	// 不排除那些 没有后缀名的文件 存在的可能;
            	// 如果切换至该目录失败,那么其可能是没有后缀名的文件,那么尝试着下载该文件
            	if (!flag) {
            		String tempWorkingDirectory = "";
            		String tempTargetFileName = "";
            		int index = remoteDirOrRemoteFile.lastIndexOf("/");
            		tempTargetFileName = remoteDirOrRemoteFile.substring(index + 1);
            		if(tempTargetFileName.length() > 0) {
	            		if (index > 0) {
	                		tempWorkingDirectory = remoteDirOrRemoteFile.substring(0, index);
	                	}else {
	                		tempWorkingDirectory = "/";
	                	}
	            		ftpClient.changeWorkingDirectory(tempWorkingDirectory);
	            		// 获取tempWorkingDirectory目录下所有 文件以及文件夹   或  获取指定的文件
	                    FTPFile[] ftpFiles = ftpClient.listFiles(tempWorkingDirectory);
	                    for(FTPFile file : ftpFiles){ 
	                    	String name = new String(file.getName().getBytes(this.downfileNameEncodingParam1), 
	                    			this.downfileNameDecodingParam2);
	                    	// 如果不是目标文件,那么不下载 
	                    	if(!tempTargetFileName.equals(name)) {
	                    		System.out.println(" FTPUtils -> a FTP File name is ---> " + name);
	                    		continue;
	                    	}
	                    	//如果文件夹不存在则创建    
	                    	if (!localFileDir.exists()) {
	                    		System.out.println(" " + localFileDir + " is not exist, create this Dir!");
	                    		localFileDir.mkdir();
	                    	}
	                        File localFile = new File(localDir + "/" + name); 
	                        os = new FileOutputStream(localFile); 
	                        boolean result = ftpClient.retrieveFile(file.getName(), os); 
	                        if (result) {
	                            successSum++;
	                        } else {
	                        	failSum++;
	                        }
	                        System.out.println(" already success download item count ---> " + successSum);
	                    } 
            		}
            		return successSum;
            	}
            }else {
            	String tempWorkingDirectory = "";
            	int index = remoteDirOrRemoteFile.lastIndexOf("/");
            	if (index > 0) {
            		tempWorkingDirectory = remoteDirOrRemoteFile.substring(0, index);
            	}else {
            		tempWorkingDirectory = "/";
            	}
            	// 切换至要下载的文件所在的目录,否者下载下来的文件大小为0
	            ftpClient.changeWorkingDirectory(tempWorkingDirectory);
            }
            // 获取remoteDirOrRemoteFile目录下所有 文件以及文件夹   或  获取指定的文件
            FTPFile[] ftpFiles = ftpClient.listFiles(remoteDirOrRemoteFile);
            for(FTPFile file : ftpFiles){ 
            	// 如果是文件夹,那么不下载 (因为:直接下载文件夹的话,是无效文件)
            	if(file.isDirectory()) {
            		continue;
            	}
            	//如果文件夹不存在则创建    
            	if (!localFileDir.exists()) {
            		System.out.println(" " + localFileDir + " is not exist, create this Dir!");
            		localFileDir.mkdir();
            	}
            	String name = new String(file.getName().getBytes(this.downfileNameEncodingParam1), 
            			this.downfileNameDecodingParam2);
                File localFile = new File(localDir + "/" + name); 
                os = new FileOutputStream(localFile); 
                boolean result = ftpClient.retrieveFile(file.getName(), os); 
                if (result) {
                    successSum++;
                } else {
                	failSum++;
                }
                System.out.println(" already success download item count ---> " + successSum);
            } 
        } finally{ 
        	// 先登出、再关闭连接
        	ftpClient.logout(); 
            if(ftpClient.isConnected()){ 
                ftpClient.disconnect();
            } 
            if(os != null){
                os.close();
            } 
        } 
        System.out.println(" FTPUtils -> downloadFile success download file total ---> " + successSum);
        System.out.println(" FTPUtils -> downloadFile fail download file total ---> " + failSum);
        return successSum; 
    }

    /**
     * downloadFile的升级版 -> 其功能如下:
     *     1.remoteDirOrRemoteFile可为FTP上某一个文件的全路径名
     *       ---> 下载该文件,此处与downloadFile功能一致
     *     
     *     2.remoteDirOrRemoteFile可为FTP上某一个文件目录名
     *       ---> 下载该目录下的所有文件、文件夹(包括该文件夹中的所有文件文件夹并以此类推) 
     *           注:对比downloadFile方法可知,downloadFile只能下载该目录下的所有文件,不能递归下载
     *
     * @DATE 2018年9月26日 下午7:26:22
     */
    public int recursiveDownloadFile(String remoteDirOrRemoteFile, String localDir) throws IOException{ 
    	int successSum = 0; 
    	// remoteDirOrRemoteFile是一个明确的文件  还是  一个目录
    	if(remoteDirOrRemoteFile.indexOf(".") >= 0) {
    		successSum = downloadFile(remoteDirOrRemoteFile, localDir);
    	}else {
	        /// 初步组装数据,调用递归方法;查询给定FTP目录以及其所有子孙目录,进而得到FTP目录与本地目录的对应关系Map
	        // 有序存放FTP remote文件夹路径
    		// 其实逻辑是:先往alreadyQueriedDirList里面存,再进行的查询。此处可以这么处理。
	        List<String> alreadyQueryDirList = new ArrayList<>(16); 
	        alreadyQueryDirList.add(remoteDirOrRemoteFile);
	        // 有序存放FTP remote文件夹路径
	        List<String> requiredQueryDirList = new ArrayList<>(16); 
	        requiredQueryDirList.add(remoteDirOrRemoteFile);
	        // 记录FTP目录与 本地目录对应关系
	        Map<String, String> storeDataMap = new HashMap<>();
	        storeDataMap.put(remoteDirOrRemoteFile, localDir);
	        queryFTPAllChildrenDirectory(storeDataMap, alreadyQueryDirList, requiredQueryDirList);
	        
	        // 循环调用downloadFile()方法,进行嵌套下载
	        for (int i = 0; i < alreadyQueryDirList.size(); i++) {
	        	int thiscount = downloadFile(alreadyQueryDirList.get(i), 
	        			storeDataMap.get(alreadyQueryDirList.get(i)));
	        	successSum += thiscount;
			}
    	}
        System.out.println(" FTPUtils -> recursiveDownloadFile(excluded created directories) "
        		               + " success download file total ---> " + successSum);
        return successSum; 
    }
 

    /**
     * 删除文件 或 删除空的文件夹
     * 注:删除不存在的目录或文件  会导致删除失败
     *
     * @param deletedDirOrFile
     *            要删除的文件的全路径名  或  要删除的空文件夹全路径名
     *        统一:路径分割符 用“/”,而不用“\”;
     *        
     * @return 删除成功与否
     * @throws IOException
     * @DATE 2018年9月26日 下午9:12:07
     */
    public boolean deleteBlankDirOrFile(String deletedBlankDirOrFile) throws IOException{ 
        boolean flag = false; 
        try { 
            initFtpClient();
            // 根据remoteDirOrRemoteFile是文件还是目录,来切换changeWorkingDirectory
            if(deletedBlankDirOrFile.lastIndexOf(".") < 0) {
	            // 出于保护机制:如果当前文件夹中是空的,那么才能删除成功
            	flag = ftpClient.removeDirectory(deletedBlankDirOrFile);
            	// 不排除那些 没有后缀名的文件 存在的可能;
            	// 如果删除空文件夹失败,那么其可能是没有后缀名的文件,那么尝试着删除文件
            	if (!flag) {
            		flag = ftpClient.deleteFile(deletedBlankDirOrFile);
            	}
            }else {/// 如果是文件,那么直接删除该文件
            	String tempWorkingDirectory = "";
            	int index = deletedBlankDirOrFile.lastIndexOf("/");
            	if (index > 0) {
            		tempWorkingDirectory = deletedBlankDirOrFile.substring(0, index);
            	}else {
            		tempWorkingDirectory = "/";
            	}
            	// 切换至要下载的文件所在的目录,否者下载下来的文件大小为0
	            ftpClient.changeWorkingDirectory(tempWorkingDirectory);
	            flag = ftpClient.deleteFile(deletedBlankDirOrFile.substring(index + 1));
            }
        } finally {
        	ftpClient.logout();
            if(ftpClient.isConnected()){ 
                ftpClient.disconnect();
            } 
        }
        if (flag == false) {
        	System.out.println(" FTPUtils -> deleteBlankDirOrFile Maybe [" + deletedBlankDirOrFile 
		               				+ "]  doesn't exist !");
        }
        System.out.println(" FTPUtils -> deleteBlankDirOrFile [" + deletedBlankDirOrFile 
        		               + "] boolean result is ---> " + flag);
        return flag; 
    }
    
    
    /**
     * deleteBlankDirOrFile的加强版 -> 可删除文件、空文件夹、非空文件夹
     *
     * @param deletedBlankDirOrFile
     * @return
     * @throws IOException
     * @DATE 2018年9月27日 上午1:25:16
     */
    public boolean recursiveDeleteBlankDirOrFile(String deletedBlankDirOrFile) throws IOException{ 
    	boolean result = true; 
    	if(!destDirExist(deletedBlankDirOrFile)) {
    		System.out.println(" " + deletedBlankDirOrFile + " maybe is a  non-suffix file!, try delete!");
    		boolean flag = deleteBlankDirOrFile(deletedBlankDirOrFile);
    		String flagIsTrue = " FTPUtils -> recursiveDeleteBlankDirOrFile " 
    		                        + deletedBlankDirOrFile + "---> success!";
    		String flagIsFalse = " FTPUtils -> recursiveDeleteBlankDirOrFile " 
                    + deletedBlankDirOrFile + "---> target file is not exist!";
    		System.out.println(flag == true ? flagIsTrue : flagIsFalse);
    		return true;
    	}
    	// remoteDirOrRemoteFile是一个明确的文件  还是  一个目录
    	if (deletedBlankDirOrFile.indexOf(".") >= 0 || !ftputilsChangeWorkingDirectory(deletedBlankDirOrFile)) {
    		result = deleteBlankDirOrFile(deletedBlankDirOrFile);
    	} else {
	        /// 初步组装数据,调用递归方法;查询给定FTP目录以及其所有子孙目录、子孙文件        (含其自身)
	        // 存放  文件夹路径
    		// 其实逻辑是:先往alreadyQueriedDirList里面存,再进行的查询。此处可以这么处理。
	        List<String> alreadyQueriedDirList = new ArrayList<>(16); 
	        alreadyQueriedDirList.add(deletedBlankDirOrFile);
	        // 存放  文件路径
	        List<String> alreadyQueriedFileList = new ArrayList<>(16); 
	        // 存放 文件夹路径
	        List<String> requiredQueryDirList = new ArrayList<>(16); 
	        requiredQueryDirList.add(deletedBlankDirOrFile);
	        queryAllChildrenDirAndChildrenFile(alreadyQueriedDirList, 
								        		alreadyQueriedFileList, 
								        		requiredQueryDirList);
	        
	        // 循环调用deleteBlankDirOrFile()方法,删除文件
	        for (int i = 0; i < alreadyQueriedFileList.size(); i++) {
	        	boolean isSuccess = deleteBlankDirOrFile(alreadyQueriedFileList.get(i));
	        	if (!isSuccess) {
	        		result = false;
	        	}
			}
	        
	        // 对alreadyQueriedDirList进行排序,以保证等下删除时,先删除的空文件夹是 最下面的
	        String[] alreadyQueriedDirArray = new String[alreadyQueriedDirList.size()];
	        alreadyQueriedDirArray = alreadyQueriedDirList.toArray(alreadyQueriedDirArray);
	        sortArray(alreadyQueriedDirArray);
	        
	        // 循环调用deleteBlankDirOrFile()方法,删除空的文件夹
	        for (int i = 0; i < alreadyQueriedDirArray.length; i++) {
	        	boolean isSuccess = deleteBlankDirOrFile(alreadyQueriedDirArray[i]);
	        	if (!isSuccess) {
	        		result = false;
	        	}
			}
    	}
        System.out.println(" FTPUtils -> recursiveDeleteBlankDirOrFile "
        		               + " boolean result is---> " + result);
        return result;
    }
    
    
    
    /*  -------------JustryDeng-------------以下为辅助方法-------------JustryDeng------------- */
    
    /**
     * 根据数组元素的长度,来进行排序(字符串长的,排在前面)
     * 数组元素不能为null
     *
     * @DATE 2018年9月27日 上午12:54:03
     */
    private void sortArray(String[] array) {
    	for (int i = 0; i < array.length - 1; i++) {
    	    for(int j = 0; j < array.length - 1 - i; j++) {
	            if (array[j].length() - array[j+1].length() < 0) {
	             String flag=array[j];
	             array[j] = array[j+1];
	             array[j+1] = flag;
	            }
    	    }
    	}
    }
    
    /**
     * 根据给出的FTP目录、对应本地目录; 查询该FTP目录的所有子目录 , 以及获得与每一个子目录对应的本地目录(含其自身以及与其自身对应的本地目录)
     *
     * @param storeDataMap
     *            存储FTP目录与本地目录的对应关系;key -> FTP目录, value -> 与key对应的本地目录
     * @param alreadyQueryDirList
     *            所有已经查询过了的FTP目录,即:key集合
     * @param requiredQueryDirList
     *            还需要查询的FTP目录
     * @throws IOException
     * @DATE 2018年9月26日 下午7:17:52
     */
    private void queryFTPAllChildrenDirectory(Map<String, String> storeDataMap, 
									    		List<String> alreadyQueriedDirList, 
									    		List<String> requiredQueryDirList) throws IOException {
    	List<String> newRequiredQueryDirList = new ArrayList<>(16); 
    	initFtpClient();
    	try { 
    		if(requiredQueryDirList.size() == 0) {
    			return;
    		}
    		for (int i = 0; i < requiredQueryDirList.size(); i++) {
    			String rootRemoteDir = requiredQueryDirList.get(i);
    			String rootLocalDir = storeDataMap.get(requiredQueryDirList.get(i));
	            // 获取rootRemoteDir目录下所有 文件以及文件夹(或  获取指定的文件)
	            FTPFile[] ftpFiles = ftpClient.listFiles(rootRemoteDir);
	            for(FTPFile file : ftpFiles){ 
	             	if (file.isDirectory()) {
	             		String tempName = file.getName();
	             		String ftpChildrenDir = "";
         				ftpChildrenDir = rootRemoteDir + "/" + tempName  ;
	             		String localChildrenDir = "";
             			localChildrenDir = rootLocalDir + "/" + tempName  ;
             			alreadyQueriedDirList.add(ftpChildrenDir);
	             		newRequiredQueryDirList.add(ftpChildrenDir);
	             		storeDataMap.put(ftpChildrenDir, localChildrenDir);
	             	}
	            } 
    		}
    	} finally{ 
    		// 先登出、再关闭连接
    		ftpClient.logout(); 
    		if (ftpClient.isConnected()){ 
    			ftpClient.disconnect();
    		} 
    	} 
    	this.queryFTPAllChildrenDirectory(storeDataMap, alreadyQueriedDirList, newRequiredQueryDirList);
    }
    
    /**
     * 根据给出的FTP目录,查询其所有子目录以及子文件(含其自身)
     *
     * @param alreadyQueriedDirList
     *            所有已经查询出来了的目录
     * @param alreadyQueriedFileList
     *            所有已经查询出来了的文件
     * @param requiredQueryDirList
     *            还需要查询的FTP目录
     * @throws IOException
     * @DATE 2018年9月27日 上午12:12:53
     */
    private void queryAllChildrenDirAndChildrenFile(List<String> alreadyQueriedDirList, 
									    		List<String> alreadyQueriedFileList, 
									    		List<String> requiredQueryDirList) throws IOException {
    	List<String> newRequiredQueryDirList = new ArrayList<>(16); 
    	initFtpClient();
    	try { 
    		if(requiredQueryDirList.size() == 0) {
    			return;
    		}
    		for (int i = 0; i < requiredQueryDirList.size(); i++) {
    			String dirPath = requiredQueryDirList.get(i);
	            // 获取dirPath目录下所有 文件以及文件夹(或  获取指定的文件)
	            FTPFile[] ftpFiles = ftpClient.listFiles(dirPath);
	            for(FTPFile file : ftpFiles){ 
	             	if (file.isDirectory()) {
	             		String tempName = file.getName();
	             		String ftpChildrenDir = dirPath + "/" + tempName;
	             		alreadyQueriedDirList.add(ftpChildrenDir);
	             		newRequiredQueryDirList.add(ftpChildrenDir);
	             	} else {
	             		String tempName = file.getName();
	             		String ftpChildrenFile = dirPath + "/" + tempName;
	             		alreadyQueriedFileList.add(ftpChildrenFile);
	             	}
	            } 
    	 
    		}
    	} finally{ 
    		// 先登出、再关闭连接
    		ftpClient.logout(); 
    		if (ftpClient.isConnected()){ 
    			ftpClient.disconnect();
    		} 
    	} 
    	this.queryAllChildrenDirAndChildrenFile(alreadyQueriedDirList, alreadyQueriedFileList, newRequiredQueryDirList);
    }

    
    /**
     * 创建指定目录(注:如果要创建的目录已经存在,那么返回false)
     *
     * @param dir
     *            目录路径,绝对路径,如: /abc 或  /abc/ 可以
     *                   相对路径,如:  sss 或    sss/ 也可以
     *                  注:相对路径创建的文件夹所在位置时,相对于当前session所处目录位置。
     *                  提示: .changeWorkingDirectory() 可切换当前session所处目录位置
     * @return 创建成功与否
     * @throws IOException 
     * @DATE 2018年9月26日 下午3:42:20
     */
    private boolean makeDirectory(String dir) throws IOException {
        boolean flag = false;
        flag = ftpClient.makeDirectory(dir);
        if (flag) {
            System.out.println(" FTPUtils -> makeDirectory -> create Dir [" + dir + "] success!");
        } else {
            System.err.println(" FTPUtils -> makeDirectory -> create Dir [" + dir + "] fail!");
        }
        return flag;
    }
    
    /**
     * 在FTP服务器上创建remoteDir目录(不存在,则创建;存在,则不创建)
     *
     * @param remoteDir
     *            要创建的目录   为null或为"" 则视为  根目录 
     * @return 结果
     * @throws IOException
     * @DATE 2018年9月26日 下午2:19:37
     */
    private boolean CreateDirecroty(String remoteDir) throws IOException {
        boolean success = true;
        String directory = null;
        if(remoteDir == null || remoteDir.trim().equals("")) {
        	directory = "/";
        }else if(remoteDir.endsWith("/")) {
        	directory = remoteDir;
        }else {
        	directory = remoteDir + "/";
        }
        // directory不为根目录 且 切换至该目录失败 -> 说明FTPServer中不存在该目录,那么进行创建
        /*
         * .changeWorkingDirectory(directory)中的directory为 要切换到的目录
         *   可为 -> 绝对路径; 可为 -> 相对路径(如果为相对路径,那么相对于当前session所处目录)
         */
        if (!directory.equals("/") && !ftpClient.changeWorkingDirectory(directory)) {
        	// 获得每一个节点目录的起始位置
            int start = 0;
            int end = 0;
            if (directory.startsWith("/")) {
                start = 1;
            } else {
                start = 0;
            }
            end = directory.indexOf("/", start);
            // 循环创建目录
            String dirPath = "";
            String paths = "";
            while (true) {
                String subDirectory = directory.substring(start, end);
                dirPath = dirPath + "/" + subDirectory;
                if (!ftpClient.changeWorkingDirectory(dirPath)) {
                    makeDirectory(dirPath);
                    ftpClient.changeWorkingDirectory(dirPath);
                    // 当前session所处FTP目录位置
                    String currentDirPath = ftpClient.printWorkingDirectory();
                    System.out.println(" FTPUtils -> current position dirPath ---> " + currentDirPath);
                } 
                // 根性子节点目录名 index起始位置
                paths = paths + "/" + subDirectory;
                start = end + 1;
                end = directory.indexOf("/", start);
                // 检查所有目录是否创建完毕
                if (end < 0) {
                    break;
                }
            }
        }
        return success;
    }
    
    
    /**
     * 避免在代码中频繁 initFtpClient、logout、disconnect;
     * 这里包装一下FTPClient的.changeWorkingDirectory(String pathname)方法
     *
     * @param directory
     *            要切换(session)到FTP的哪一个目录下
     * @DATE 2018年9月27日 上午11:24:25
     */
    private boolean ftputilsChangeWorkingDirectory(String pathname) throws IOException{
    	boolean result = true;
	    try {
	    	initFtpClient();
	    	result = ftpClient.changeWorkingDirectory(pathname);
	    }finally{
	    	// 先登出、再关闭连接
	    	ftpClient.logout();
	        if(ftpClient.isConnected()){ 
	            ftpClient.disconnect();
	        }
	    }
	        return result;
	}
    
    /**
     * 判断FTP上某目录是否存在
     *
     * @param pathname
     *            要判断的路径(文件名全路径、文件夹全路径都可以)
     *            注:此路径应从根目录开始
     * @DATE 2018年9月27日 上午11:24:25
     */
    private boolean destDirExist(String pathname) throws IOException{
    	boolean result = true;
	    try {
	    	// 初始化时,当前session位置即为 “/”
	    	initFtpClient();
	    	if (!pathname.startsWith("/")) {
	    		pathname = "/" + pathname;
	    	}
	    	if (pathname.lastIndexOf(".") >= 0) {
	    		int index = pathname.lastIndexOf("/");
	    		if (index != 0) {
	    			pathname = pathname.substring(0, index);
	    		} else {
	    			return true;
	    		}
	    	}
	    	result = ftpClient.changeWorkingDirectory(pathname);
	    }finally{
	    	// 先登出、再关闭连接
	    	ftpClient.logout();
	        if(ftpClient.isConnected()){ 
	            ftpClient.disconnect();
	        }
	    }
	    return result;
	}
}

微笑参考链接
            https://www.cnblogs.com/weihbs/p/7760552.html
微笑代码(工具类、以及测试代码)托管链接

            https://github.com/JustryDeng/PublicRepository

微笑如有不当之处,欢迎指正

微笑本文已经被收录进《程序员成长笔记(三)》,笔者JustryDeng

猜你喜欢

转载自blog.csdn.net/justry_deng/article/details/82866777