近期工作使用到的FTP上传下载总结

轨道检测系统需要用到的FTP上传下载, 能够正常使用.

 

package task.util;



import org.apache.commons.net.ftp.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * FTP上传、下载、删除 工具类
 * 约定:统一使用绝对路径
 *
 * @author JustryDeng
 * @date 2018年9月26日 上午10:38:46
 */
public class FtpUtil {

    private final Logger LOGGER = LoggerFactory.getLogger(FtpUtil.class);

    /** 路径分隔符 */
    private final String SEPARATOR_STR = "/";

    /** 点 */
    private final String DOT_STR = ".";

    /** ftp服务器地址 */
    private String hostname;

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

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

    /** ftp登录密码 */
    private String password;

    /**
     * 命令 语句 编码(控制发出去的命令的编码)
     * 如:在删除时,发出去的指令由于此处的编码不对应的原因,乱码了;(找不到目标文件)导致删除失败
     * 如:在下载时,发出去的指令由于此处的编码不对应的原因,乱码了;(找不到目标文件)导致下载失败
     * 如:在上传时,发出去的指令由于此处的编码不对应的原因,乱码了;导致上传到FTP的文件的文件名乱码
     *
     * 注:根据不同的(Server/Client)情况,这里灵活设置
     */
    private String sendCommandStringEncoding = "UTF-8";

    /**
     * 下载文件,文件名encode编码
     *
     * 注:根据不同的(Server/Client)情况,这里灵活设置
     */
    private String downfileNameEncodingParam1 = "UTF-8";

    /**
     * 下载文件,文件名decode编码
     *
     * 注:根据不同的(Server/Client)情况,这里灵活设置
     */
    private String downfileNameDecodingParam2 = "UTF-8";

    /**
     * 设置文件传输形式(使用FTP类静态常量赋值即可)
     *
     * 注:根据要下载上传的文件情况,这里灵活设置
     */
    private Integer transportFileType = FTP.BINARY_FILE_TYPE;

    /**
     * 用户FTP对应的根目录, 形如 /var/ftpusers/justry_deng_root
     * 注:初始化之后,其值不可能为 null
     *
     */
    private String userRootDir;

    /** FTP客户端 */
    private FTPClient ftpClient;

    private FtpUtil(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 端口
     * @param username
     *            用户名
     * @param password
     *            密码
     * @return FtpUtil实例
     * @date 2018年9月26日 下午4:39:02
     */
    public static FtpUtil getFtpUtilInstance(String hostname, Integer port, String username, String password) {
        return new FtpUtil(hostname, port, username, password);
    }

    /**
     * 初始化FTP服务器
     *
     * @throws IOException IO异常
     * @date 2018年9月26日 下午1:37:14
     */
    private void initFtpClient() throws IOException {
        if(ftpClient == null){
            ftpClient = new FTPClient();
        }
        // 设置编码(其是影响是否乱码的主要因素,在ftpClient.connect(hostname, port)连接前就要设置,否者不一定生效)
        ftpClient.setControlEncoding(sendCommandStringEncoding);
        // Returns the integer value of the reply code of the last FTP reply.
        int replyCode = ftpClient.getReplyCode();
        // 221表示 退出了网络,那么需要重新连接
        int ftpDisconnectionStatusCode = 221;
        // Determine if a reply code is a positive completion response.
        // FTPReply.isPositiveCompletion(replyCode)可用于验证是否连接FTP
        if (FTPReply.isPositiveCompletion(replyCode) && replyCode != ftpDisconnectionStatusCode) {
            LOGGER.info(" FtpUtil -> alreadly connected FTPServer !");
            return;
        } else {
            LOGGER.info(" FtpUtil -> connecting FTPServer -> {} : {}", this.hostname, this.port);
            // 连接ftp服务器
            ftpClient.connect(hostname, port);
            // 登录ftp服务器
            ftpClient.login(username, password);
            LOGGER.info(" FtpUtil -> connect FTPServer success!");
            // 初始化ftp用户根目录, 形如 /var/ftpusers/justry_deng_root
            userRootDir = ftpClient.printWorkingDirectory();
            if (userRootDir == null || "".equals(userRootDir.trim()) || SEPARATOR_STR.equals(userRootDir)) {
                userRootDir = "";
            }
        }
        // 设置文件传输形式
        ftpClient.setFileType(transportFileType);
        // 设置文件传输形式
        ftpClient.setFileType(transportFileType);
        // 设置FTP客户端(即本地)模式为被动模式
        ftpClient.enterLocalPassiveMode();
        // 由于apache不支持中文语言环境,通过定制类解析中文日期类型
        ftpClient.configure(new FTPClientConfig(FTPClientConfig.SYST_NT));
    }

    /**
     * 上传文件至FTP
     * 注:若有同名文件,那么原文件会被覆盖
     *
     * @param remoteDir
     *            上传到指定目录(FTP用户pwd时的绝对路径)
     * @param remoteFileName
     *            上传到FTP,该文件的文件名
     * @param file
     *            要上传的本地文件
     *
     * @return 上传结果
     * @throws IOException IO异常
     * @date 2018年9月26日 下午1:35:27
     */
    @SuppressWarnings("unused")
    public boolean uploadFile(String remoteDir, String remoteFileName, File file) throws IOException{
        boolean result;
        remoteDir = handleRemoteDir(remoteDir);
        try(InputStream inputStream = new FileInputStream(file)){
            // 初始化
            initFtpClient();
            createDirecroty(remoteDir);
            ftpClient.changeWorkingDirectory(remoteDir);
            result = ftpClient.storeFile(remoteFileName, inputStream);
        }
        LOGGER.info(" FtpUtil -> uploadFile boolean result is -> {}", result);
        return result;
    }

    /**
     * 从FTP下载文件
     * 注:如果remoteDirOrRemoteFile不存在,则不会下载下来任何东西
     * 注:如果remoteDirOrRemoteFile不存在,localDir也不存在;再不会下载下来任何东西,
     *    也不会在本地创建localDir目录
     *
     * @param remoteDirOrRemoteFile
     *            FTP中的某一个目录(此时下载该目录下的所有文件,该目录下的文件夹不会被下载);
     *            或  FTP中的某一个文件全路径名(此时下载该文件)
     * @param localDir
     *            本地用于保存下载下来的文件的文件夹
     *
     * @return 下载了的文件个数
     * @throws IOException IO异常
     * @date 2018年9月26日 下午7:24:11
     */
    public int downloadFile(String remoteDirOrRemoteFile, String localDir) throws IOException{
        remoteDirOrRemoteFile = handleRemoteDir(remoteDirOrRemoteFile);
        int successSum = 0;
        int failSum = 0;
        initFtpClient();
        // 根据remoteDirOrRemoteFile是文件还是目录,来切换changeWorkingDirectory
        if (!remoteDirOrRemoteFile.contains(DOT_STR)) {
            // 切换至要下载的文件所在的目录,否者下载下来的文件大小为0
            boolean flag = ftpClient.changeWorkingDirectory(remoteDirOrRemoteFile);
            // 不排除那些 没有后缀名的文件 存在的可能;
            // 如果切换至该目录失败,那么其可能是没有后缀名的文件,那么尝试着下载该文件
            if (!flag) {
                return downloadNonsuffixFile(remoteDirOrRemoteFile, localDir);
            }
        } else {
            String tempWorkingDirectory;
            int index = remoteDirOrRemoteFile.lastIndexOf(SEPARATOR_STR);
            if (index > 0) {
                tempWorkingDirectory = remoteDirOrRemoteFile.substring(0, index);
            } else {
                tempWorkingDirectory = SEPARATOR_STR;
            }
            // 切换至要下载的文件所在的目录,否者下载下来的文件大小为0
            ftpClient.changeWorkingDirectory(tempWorkingDirectory);
        }
        File localFileDir = new File(localDir);
       // ftpClient.configure(new FTPClientConfig("com.sime.util.UnixFTPEntryParser")); //这里记得改成你放的位置
        // 获取remoteDirOrRemoteFile目录下所有 文件以及文件夹   或  获取指定的文件
        FTPFile[] ftpFiles = ftpClient.listFiles(remoteDirOrRemoteFile);
        for (FTPFile file : ftpFiles) {
            // 如果是文件夹,那么不下载 (因为:直接下载文件夹的话,是无效文件)
            if (file.isDirectory()) {
                continue;
            }
            //如果文件夹不存在则创建    
            if (!localFileDir.exists()) {
                boolean result = localFileDir.mkdirs();
                LOGGER.info(" {} is not exist, create this Dir! create result -> {}!",
                        localFileDir, result);
            }
            String name = new String(file.getName().getBytes(this.downfileNameEncodingParam1),
                    this.downfileNameDecodingParam2);
            String tempLocalFile = localDir.endsWith(SEPARATOR_STR) ?
                    localDir + name :
                    localDir + SEPARATOR_STR + name;
            File localFile = new File(tempLocalFile);
            try (OutputStream os = new FileOutputStream(localFile)) {
                boolean result = ftpClient.retrieveFile(file.getName(), os);
                if (result) {
                    successSum++;
                    LOGGER.info(" already download normal file -> {}", name);
                } else {
                    failSum++;
                }
            }
        }
        LOGGER.info(" FtpUtil -> downloadFile success download file total -> {}", successSum);
        LOGGER.info(" FtpUtil -> 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 {
        remoteDirOrRemoteFile = handleRemoteDir(remoteDirOrRemoteFile);
        int successSum = 0;
        // remoteDirOrRemoteFile是一个明确的文件  还是  一个目录
        if (remoteDirOrRemoteFile.contains(DOT_STR)) {
            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<>(16);
            storeDataMap.put(remoteDirOrRemoteFile, localDir);
            queryFTPAllChildrenDirectory(storeDataMap, alreadyQueryDirList, requiredQueryDirList);
            String tempPath;
            // 循环调用downloadFile()方法,进行嵌套下载
            for(String str : alreadyQueryDirList) {
                // 将FTP用户的pwd的绝对路径转换为,用户输入的路径(因为 downloadFile方法会将用户输入的路径转化为pwd路径)
                // 提示:用户刚登陆进FTP时,输入pwd,得到的不一定是"/",有可能时FTP对应的Linux上的文件夹路径,
                //     这与FTP的设置有关,可详见《程序员成长笔记(四)》搭建FTP服务器相关章节
                tempPath = str.length() > userRootDir.length() ?
                        str.substring(userRootDir.length()) :
                        SEPARATOR_STR;
                int thiscount = downloadFile(tempPath, storeDataMap.get(str));
                successSum += thiscount;
            }
        }
        System.out.println(" FtpUtil -> recursiveDownloadFile(excluded created directories) "
                + " success download file total -> " + successSum);
        return successSum;
    }

    /**
     * 删除文件 或 删除空的文件夹
     * 注:删除不存在的目录或文件  会导致删除失败
     * 注: 出于保护措施,输入的“/”时,不可删除,直接返回false
     *
     * @param deletedBlankDirOrFile
     *            要删除的文件的全路径名  或  要删除的空文件夹全路径名
     *        统一:路径分割符 用“/”,而不用“\”;
     *
     * @return 删除成功与否
     * @throws IOException IO异常
     * @date 2018年9月26日 下午9:12:07
     */
    public boolean deleteBlankDirOrFile(String deletedBlankDirOrFile) throws IOException{
        if(deletedBlankDirOrFile == null || SEPARATOR_STR.equals(deletedBlankDirOrFile)) {
            return false;
        }
        deletedBlankDirOrFile = handleRemoteDir(deletedBlankDirOrFile);
        boolean flag;
        initFtpClient();
        // 根据remoteDirOrRemoteFile是文件还是目录,来切换changeWorkingDirectory
        if (deletedBlankDirOrFile.lastIndexOf(DOT_STR) < 0) {
            // 出于保护机制:如果当前文件夹中是空的,那么才能删除成功
            flag = ftpClient.removeDirectory(deletedBlankDirOrFile);
            // 不排除那些 没有后缀名的文件 存在的可能;
            // 如果删除空文件夹失败,那么其可能是没有后缀名的文件,那么尝试着删除文件
            if (!flag) {
                flag = ftpClient.deleteFile(deletedBlankDirOrFile);
            }
            // 如果是文件,那么直接删除该文件
        } else {
            String tempWorkingDirectory;
            int index = deletedBlankDirOrFile.lastIndexOf(SEPARATOR_STR);
            if (index > 0) {
                tempWorkingDirectory = deletedBlankDirOrFile.substring(0, index);
            } else {
                tempWorkingDirectory = SEPARATOR_STR;
            }
            // 切换至要下载的文件所在的目录,否者下载下来的文件大小为0
            ftpClient.changeWorkingDirectory(tempWorkingDirectory);
            flag = ftpClient.deleteFile(deletedBlankDirOrFile.substring(index + 1));
        }
        LOGGER.info(" FtpUtil -> deleteBlankDirOrFile [{}] boolean result is -> {}",
                deletedBlankDirOrFile, flag);
        return flag;
    }


    /**
     * deleteBlankDirOrFile的加强版 -> 可删除文件、空文件夹、非空文件夹
     * 注: 出于保护措施,输入的“/”时,不可删除,直接返回false
     *
     * @param deletedBlankDirOrFile
     *            要删除的文件路径或文件夹
     *
     * @return 删除成功与否
     * @throws IOException IO异常
     * @date 2018年9月27日 上午1:25:16
     */
    public boolean recursiveDeleteBlankDirOrFile(String deletedBlankDirOrFile) throws IOException{
        if(deletedBlankDirOrFile == null || SEPARATOR_STR.equals(deletedBlankDirOrFile)) {
            return false;
        }
        String realDeletedBlankDirOrFile = handleRemoteDir(deletedBlankDirOrFile);
        boolean result = true;
        initFtpClient();
        if(!destDirExist(realDeletedBlankDirOrFile)) {
            LOGGER.info(" {} maybe is a  non-suffix file!, try delete!", realDeletedBlankDirOrFile);
            boolean flag = deleteBlankDirOrFile(deletedBlankDirOrFile);
            String flagIsTrue = " FtpUtil -> recursiveDeleteBlankDirOrFile "
                    + realDeletedBlankDirOrFile + " -> success!";
            String flagIsFalse = " FtpUtil -> recursiveDeleteBlankDirOrFile "
                    + realDeletedBlankDirOrFile + " -> target file is not exist!";
            LOGGER.info(flag ? flagIsTrue : flagIsFalse);
            return true;
        }
        // remoteDirOrRemoteFile是一个明确的文件  还是  一个目录
        if (realDeletedBlankDirOrFile.contains(DOT_STR) || !ftputilsChangeWorkingDirectory(realDeletedBlankDirOrFile)) {
            result = deleteBlankDirOrFile(deletedBlankDirOrFile);
        } else {
            /// 初步组装数据,调用递归方法;查询给定FTP目录以及其所有子孙目录、子孙文件        (含其自身)
            // 存放  文件夹路径
            // 其实逻辑是:先往alreadyQueriedDirList里面存,再进行的查询。此处可以这么处理。
            List<String> alreadyQueriedDirList = new ArrayList<>(16);
            alreadyQueriedDirList.add(realDeletedBlankDirOrFile);
            // 存放  文件路径
            List<String> alreadyQueriedFileList = new ArrayList<>(16);
            // 存放 文件夹路径
            List<String> requiredQueryDirList = new ArrayList<>(16);
            requiredQueryDirList.add(realDeletedBlankDirOrFile);
            queryAllChildrenDirAndChildrenFile(alreadyQueriedDirList,
                    alreadyQueriedFileList,
                    requiredQueryDirList);
            String tempPath;
            // 循环调用deleteBlankDirOrFile()方法,删除文件
            for (String filePath : alreadyQueriedFileList) {
                tempPath = filePath.length() > userRootDir.length() ?
                        filePath.substring(userRootDir.length()) :
                        SEPARATOR_STR;
                deleteBlankDirOrFile(tempPath);
            }
            // 对alreadyQueriedDirList进行排序,以保证等下删除时,先删除的空文件夹是 最下面的
            String[] alreadyQueriedDirArray = new String[alreadyQueriedDirList.size()];
            alreadyQueriedDirArray = alreadyQueriedDirList.toArray(alreadyQueriedDirArray);
            sortArray(alreadyQueriedDirArray);
            // 循环调用deleteBlankDirOrFile()方法,删除空的文件夹
            for (String str : alreadyQueriedDirArray) {
                tempPath = str.length() > userRootDir.length() ?
                        str.substring(userRootDir.length()) :
                        SEPARATOR_STR;
                boolean isSuccess = deleteBlankDirOrFile(tempPath);
                if (!isSuccess) {
                    result = false;
                }
            }
        }
        LOGGER.info(" FtpUtil -> recursiveDeleteBlankDirOrFile {} boolean result is -> {}",
                realDeletedBlankDirOrFile, result);
        return result;
    }

    /**
     * 根据数组元素的长度,来进行排序(字符串长的,排在前面)
     * 数组元素不能为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 alreadyQueriedDirList
     *            所有已经查询过了的FTP目录,即:key集合
     * @param requiredQueryDirList
     *            还需要查询的FTP目录
     *
     * @throws IOException IO异常
     * @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);
        if(requiredQueryDirList.size() == 0) {
            return;
        }
        for (String str : requiredQueryDirList) {
            String rootLocalDir = storeDataMap.get(str);
            // 获取rootRemoteDir目录下所有 文件以及文件夹(或  获取指定的文件)
            FTPFile[] ftpFiles = ftpClient.listFiles(str);
            for(FTPFile file : ftpFiles){
                if (file.isDirectory()) {
                    String tempName = file.getName();
                    String ftpChildrenDir = str.endsWith(SEPARATOR_STR) ?
                            str + tempName :
                            str + SEPARATOR_STR + tempName;
                    String localChildrenDir = rootLocalDir.endsWith(SEPARATOR_STR) ?
                            rootLocalDir + tempName :
                            rootLocalDir + SEPARATOR_STR + tempName;
                    alreadyQueriedDirList.add(ftpChildrenDir);
                    newRequiredQueryDirList.add(ftpChildrenDir);
                    storeDataMap.put(ftpChildrenDir, localChildrenDir);
                }
            }
        }
        this.queryFTPAllChildrenDirectory(storeDataMap, alreadyQueriedDirList, newRequiredQueryDirList);
    }

    /**
     * 根据给出的FTP目录,查询其所有子目录以及子文件(含其自身)
     *
     * @param alreadyQueriedDirList
     *            所有已经查询出来了的目录
     * @param alreadyQueriedFileList
     *            所有已经查询出来了的文件
     * @param requiredQueryDirList
     *            还需要查询的FTP目录
     * @throws IOException IO异常
     * @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);
        if (requiredQueryDirList.size() == 0) {
            return;
        }
        initFtpClient();
        for (String dirPath : requiredQueryDirList) {
            // 获取dirPath目录下所有 文件以及文件夹(或  获取指定的文件)
            FTPFile[] ftpFiles = ftpClient.listFiles(dirPath);
            for (FTPFile file : ftpFiles) {
                String tempName = file.getName();
                String ftpChildrenName = dirPath.endsWith(SEPARATOR_STR) ?
                        dirPath + tempName :
                        dirPath + SEPARATOR_STR + tempName;
                if (file.isDirectory()) {
                    alreadyQueriedDirList.add(ftpChildrenName);
                    newRequiredQueryDirList.add(ftpChildrenName);
                } else {
                    alreadyQueriedFileList.add(ftpChildrenName);
                }
            }

        }
        this.queryAllChildrenDirAndChildrenFile(alreadyQueriedDirList, alreadyQueriedFileList, newRequiredQueryDirList);
    }


    /**
     * 创建指定目录(注:如果要创建的目录已经存在,那么返回false)
     *
     * @param dir
     *            目录路径,绝对路径,如: /abc 或  /abc/ 可以
     *                   相对路径,如:  sss 或    sss/ 也可以
     *                  注:相对路径创建的文件夹所在位置时,相对于当前session所处目录位置。
     *                  提示: .changeWorkingDirectory() 可切换当前session所处目录位置
     * @return 创建成功与否
     * @throws IOException IO异常
     * @date 2018年9月26日 下午3:42:20
     */
    private boolean makeDirectory(String dir) throws IOException {
        boolean flag;
        flag = ftpClient.makeDirectory(dir);
        if (flag) {
            LOGGER.info(" FtpUtil -> makeDirectory -> create Dir [{}] success!", dir);
        } else {
            LOGGER.info(" FtpUtil -> makeDirectory -> create Dir [{}] fail!", dir);
        }
        return flag;
    }

    /**
     * 在FTP服务器上创建remoteDir目录(不存在,则创建;存在,则不创建)
     *
     * @param directory
     *            要创建的目录
     *            注:此目录指的是FTP用户pwd时获取到的目录(这也与FTP服务器设置是否允许用户切出上级目录有关)
     *            注:pwdRemoteDir不能为null或“”,pwdRemoteDir必须是绝对路径
     *
     * @throws IOException IO异常
     * @date 2018年9月26日 下午2:19:37
     */
    public void createDirecroty(String directory) throws IOException {
        if (!directory.equals(userRootDir) && !ftpClient.changeWorkingDirectory(directory)) {
            if (!directory.endsWith(SEPARATOR_STR)) {
                directory = directory  + SEPARATOR_STR;
            }
            // 获得每一个节点目录的起始位置
            int start = userRootDir.length() + 1;
            int end = directory.indexOf(SEPARATOR_STR, start);
            // 循环创建目录
            String dirPath = userRootDir;
            String subDirectory;
            boolean result;
            while (end >= 0) {
                subDirectory = directory.substring(start, end);
                dirPath = dirPath + SEPARATOR_STR + subDirectory;
                if (!ftpClient.changeWorkingDirectory(dirPath)) {
                    result = makeDirectory(dirPath);
                    LOGGER.info(" FtpUtil -> createDirecroty -> invoke makeDirectory got retrun -> {}!", result);
                }
                start = end + 1;
                end = directory.indexOf(SEPARATOR_STR, start);
            }
        }
    }


    /**
     * 避免在代码中频繁 initFtpClient、logout、disconnect;
     * 这里包装一下FTPClient的.changeWorkingDirectory(String pathname)方法
     *
     * @param pathname
     *            要切换(session)到FTP的哪一个目录下
     * @date 2018年9月27日 上午11:24:25
     */
    private boolean ftputilsChangeWorkingDirectory(String pathname) throws IOException{
        boolean result;
        initFtpClient();
        result = ftpClient.changeWorkingDirectory(pathname);
        return result;
    }

    /**
     * 判断FTP上某目录是否存在
     *
     * @param pathname
     *            要判断的路径(文件名全路径、文件夹全路径都可以)
     *            注:此路径应从根目录开始
     * @date 2018年9月27日 上午11:24:25
     */
    private boolean destDirExist(String pathname) throws IOException{
        boolean result;
        if (pathname.contains(DOT_STR)) {
            int index = pathname.lastIndexOf(SEPARATOR_STR);
            if (index != 0) {
                pathname = pathname.substring(0, index);
            } else {
                return true;
            }
        }
        result = ftpClient.changeWorkingDirectory(pathname);
        return result;
    }

    /**
     * 处理用户输入的 FTP路径
     * 注:这主要是为了 兼容  FTP(对是否允许用户切换到上级目录)的设置
     *
     * @param remoteDirOrFile
     *            用户输入的FTP路径
     * @return  处理后的路径
     * @date 2019/2/1 14:00
     */
    private String handleRemoteDir(String remoteDirOrFile) throws IOException {
        initFtpClient();
        if(remoteDirOrFile == null
                || "".equals(remoteDirOrFile.trim())
                || SEPARATOR_STR.equals(remoteDirOrFile)) {
            remoteDirOrFile = userRootDir + SEPARATOR_STR;
        } else if(remoteDirOrFile.startsWith(SEPARATOR_STR)) {
            remoteDirOrFile = userRootDir + remoteDirOrFile;
        } else {
            remoteDirOrFile = userRootDir + SEPARATOR_STR + remoteDirOrFile;
        }
        return remoteDirOrFile;
    }

    /**
     * 下载 无后缀名的文件
     *
     * @param remoteDirOrFile
     *            经过handleRemoteDir()方法处理后的   FTP绝对路径
     *
     * @return  成功条数
     * @throws IOException IO异常
     * @date 2019/2/1 14:23
     */
    private int downloadNonsuffixFile(String remoteDirOrFile, String localDir) throws IOException {
        int successSum = 0;
        int failSum = 0;
        File localFileDir = new File(localDir);
        String tempWorkingDirectory;
        String tempTargetFileName;
        int index = remoteDirOrFile.lastIndexOf(SEPARATOR_STR);
        tempTargetFileName = remoteDirOrFile.substring(index + 1);
        if(tempTargetFileName.length() > 0) {
            if (index > 0) {
                tempWorkingDirectory = remoteDirOrFile.substring(0, index);
            }else {
                tempWorkingDirectory = SEPARATOR_STR;
            }
            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)) {
                    continue;
                }
                //如果文件夹不存在则创建    
                if (!localFileDir.exists()) {
                    boolean result = localFileDir.mkdirs();
                    LOGGER.info(" {} is not exist, create this Dir! create result -> {}!",
                            localFileDir, result);
                }
                String tempLocalFile = localDir.endsWith(SEPARATOR_STR) ?
                        localDir + name :
                        localDir + SEPARATOR_STR + name;
                File localFile = new File(tempLocalFile);
                try (OutputStream os = new FileOutputStream(localFile)) {
                    boolean result = ftpClient.retrieveFile(file.getName(), os);
                    if (result) {
                        successSum++;
                        LOGGER.info(" already download nonsuffixname file -> {}", name);
                    } else {
                        failSum++;
                    }
                }
                LOGGER.info(" FtpUtil -> downloadFile success download item count -> {}", successSum);
                LOGGER.info(" FtpUtil -> downloadFile fail download item count -> {}", failSum);
            }
        }
        return successSum;
    }

    /**
     * 释放资源
     *
     * 注:考虑到 递归下载、递归删除,如果将释放资源写在下载、删除逻辑里面,那么
     *    当文件较多时,就不频繁的连接FTP、断开FTP;那么久非常影响效率;
     *    所以干脆提供一个方法,在使用FTP结束后,需要主动调用此方法 释放资源
     *
     *
     */
    public void releaseResource() throws IOException {
        if (ftpClient == null) {
            return;
        }
        try {
            ftpClient.logout();
        } catch (IOException e) {
            // 连接未打开的话
            // 忽略 java.io.IOException: Connection is not open
        }
        if (ftpClient.isConnected()) {
            ftpClient.disconnect();
        }
        ftpClient = null;
    }
}

 

调用测试类

	/**
	 * ftpDownLoad
	 */
	public int downloadTest(String ftpPath, String localPath) throws IOException {
		//成功下载的文件个数
		int successSum;
		FtpUtil ftpUtil = FtpUtil.getFtpUtilInstance(PropertyUtil.getProperty("FTP_IP"), Integer.parseInt(PropertyUtil.getProperty("FTP_PORT")), PropertyUtil.getProperty("FTP_USERNAME"), PropertyUtil.getProperty("FTP_PASSWORD"));

		ftpUtil.setDownfileNameEncodingParam1("UTF-8");

		ftpUtil.setDownfileNameDecodingParam2("UTF-8");

		ftpUtil.setSendCommandStringEncoding("UTF-8");
		try {
			successSum = ftpUtil.downloadFile(ftpPath, localPath);
		} finally {
			ftpUtil.releaseResource();
		}
		return successSum;
	}



	/**
	 * 删除文件夹并且创建同名空文件夹
	 * ftpDelete
	 */
	public boolean ftpdeleteAndCreateTest(String deletedBlankDirOrFile) throws IOException {

		FtpUtil ftpUtil = FtpUtil.getFtpUtilInstance(PropertyUtil.getProperty("FTP_IP"), Integer.parseInt(PropertyUtil.getProperty("FTP_PORT")), PropertyUtil.getProperty("FTP_USERNAME"), PropertyUtil.getProperty("FTP_PASSWORD"));

		ftpUtil.setSendCommandStringEncoding("UTF-8");
		try {
			ftpUtil.recursiveDeleteBlankDirOrFile(deletedBlankDirOrFile);
			ftpUtil.createDirecroty(deletedBlankDirOrFile);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			ftpUtil.releaseResource();
		}
		return true;
	}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值