Apache Commons Net 实现 FTP 上传/下载/删除/同步

本文导读

commons-net 简述

Features(特征/特点)

Supported protocols include(支持的协议包括):

  • FTP/FTPS
  • FTP over HTTP (experimental)
  • NNTP
  • SMTP(S)
  • POP3(S)
  • IMAP(S)
  • Telnet
  • TFTP
  • Finger
  • Whois
  • rexec/rcmd/rlogin
  • Time (rdate) and Daytime
  • Echo
  • Discard
  • NTP/SNTP

FTP (package: examples/ftp)(官方关于FTP的使用例子)

  • FTPClientExample demonstrates file download and upload, LIST, MLST etc over FTP(S) and FTP over HTTP(这是一个完整的FTP客户端例子,演示与FTP服务器进行文件上传与下载、以及文件列表等功能’)
  • ServerToServerFTP This program arranges a server to server file transfer that transfers a file from host1 to host2.(这个例子演示 FTP 服务器与服务器之间的文件传输)
  • TFTPExample This is an example of a simple Java tftp client (这是一个简单的 TFTP 客户端例子)

开发包获取

Apache Commons Net 3.6 (Requires Java 1.6 or later)

Binaries

commons-net-3.6-bin.tar.gz md5 pgp
commons-net-3.6-bin.zip md5 pgp
  • 如下所示为下载之后的结构
  1. apidoc:帮助文档
  2. examples:官方示例的源码
  3. commons-net-3.6.jar:开发所需的包,必导
  4. commons-net-3.6-sources.jar:开发包的源码,可以用于学习
  5. commons-examples.jar:官方示例

Maven 依赖

<!-- https://mvnrepository.com/artifact/commons-net/commons-net -->
<dependency>
    <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.6</version>
</dependency>

环境准备

  • 为了思路清晰,本文将新建项目 Java SE,并使用 Maven 管理。
  • 采用 Maven 3.5.2 + JDK 1.8 + commos-net-3.6
C:\Users\Administrator.SC-201707281232>mvn -v
Apache Maven 3.5.2 (138edd61fd100ec658bfa2d307c43b76940a5d7d; 2017-10-18T15:58:13+08:00)
Maven home: D:\apache-maven-3.5.2\bin\..
Java version: 1.8.0_101, vendor: Oracle Corporation
Java home: D:\Java\jdk1.8.0_101\jre
Default locale: zh_CN, platform encoding: GBK
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

C:\Users\Administrator.SC-201707281232>

新建项目

项目设置

  • 如下所示补全 Maven 约定:

pom. xml 依赖

  • 使用 commons-net 的 FTP 功能,原则上导入 commons-net 依赖即可
  • 为了操作 字符串 与 文件方便,引入了 commons-lang3、commons-io,单纯使用 FTP 功能,可以用它们
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>www.wmx.com</groupId>
    <artifactId>ftpSync</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>ftpSync</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
    </properties>

    <dependencies>

        <!-- 导入 commons-net 依赖-->
        <!-- https://mvnrepository.com/artifact/commons-net/commons-net -->
        <dependency>
            <groupId>commons-net</groupId>
            <artifactId>commons-net</artifactId>
            <version>3.6</version>
        </dependency>

        <!-- 引入 Apache 的 commons-lang3 包,方便操作字符串-->
        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.8</version>
        </dependency>

        <!-- 引入 Apache commons-io 包,方便操作文件-->
        <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>

        <!-- 默认导入的 Junit 测试依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <!-- 下面都是 Maven 项目创建好之后,默认生成的插件-->
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
            <plugins>
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.0.0</version>
                </plugin>
                <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.7.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.20.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

FTP 常见操作

  • 以 FTP 连接、文件上传、文件下载、文件同步、文件删除 等为例进行说明

FTP 连接

package www.wmx.com.util;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

import java.io.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.ResourceBundle;

/**
 * Created by Administrator on 2018/8/29 0029.
 * FTP 工具类
 */
public class FTPUtil {
    /**
     * 连接 FTP 服务器
     *
     * @param addr     FTP 服务器 IP 地址
     * @param port     FTP 服务器端口号
     * @param username 登录用户名
     * @param password 登录密码
     * @return
     * @throws Exception
     */
    public static FTPClient connectFtpServer(String addr, int port, String username, String password, String controlEncoding) {
        FTPClient ftpClient = new FTPClient();
        try {
            /**设置文件传输的编码*/
            ftpClient.setControlEncoding(controlEncoding);

            /**连接 FTP 服务器
             * 如果连接失败,则此时抛出异常,如ftp服务器服务关闭时,抛出异常:
             * java.net.ConnectException: Connection refused: connect*/
            ftpClient.connect(addr, port);
            /**登录 FTP 服务器
             * 1)如果传入的账号为空,则使用匿名登录,此时账号使用 "Anonymous",密码为空即可*/
            if (StringUtils.isBlank(username)) {
                ftpClient.login("Anonymous", "");
            } else {
                ftpClient.login(username, password);
            }

            /** 设置传输的文件类型
             * BINARY_FILE_TYPE:二进制文件类型
             * ASCII_FILE_TYPE:ASCII传输方式,这是默认的方式
             * ....
             */
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);

            /**
             * 确认应答状态码是否正确完成响应
             * 凡是 2开头的 isPositiveCompletion 都会返回 true,因为它底层判断是:
             * return (reply >= 200 && reply < 300);
             */
            int reply = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                /**
                 * 如果 FTP 服务器响应错误 中断传输、断开连接
                 * abort:中断文件正在进行的文件传输,成功时返回 true,否则返回 false
                 * disconnect:断开与服务器的连接,并恢复默认参数值
                 */
                ftpClient.abort();
                ftpClient.disconnect();
            } else {
            }
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println(">>>>>FTP服务器连接登录失败,请检查连接参数是否正确,或者网络是否通畅*********");
        }
        return ftpClient;
    }

    /**
     * 使用完毕,应该及时关闭连接
     * 终止 ftp 传输
     * 断开 ftp 连接
     *
     * @param ftpClient
     * @return
     */
    public static FTPClient closeFTPConnect(FTPClient ftpClient) {
        try {
            if (ftpClient != null && ftpClient.isConnected()) {
                ftpClient.abort();
                ftpClient.disconnect();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ftpClient;
    }

    public static void main(String[] args) throws Exception {
        System.out.println("-----------------------应用启动------------------------");
        FTPClient ftpClient = FTPUtil.connectFtpServer("192.168.1.20", 21, "ftpChina", "ftpChina123456", "gbk");
        System.out.println("FTP 连接是否成功:" + ftpClient.isConnected());
        System.out.println("FTP 连接是否有效:" + ftpClient.isAvailable());
        closeFTPConnect(ftpClient);
        System.out.println("-----------------------应用关闭------------------------");
    }
}

控制台输出:

-----------------------应用启动------------------------
FTP 连接是否成功:true
FTP 连接是否有效:true
-----------------------应用关闭------------------------

Process finished with exit code 0

单个文件下载

  • FTP 客户端从 FTP 服务器下载文件,必须确保 FTP服务器给登录用户提供了"读取"权限

    /**
     * 下载 FTP 服务器上指定的单个文件,而且本地存放的文件相对部分路径 会与 FTP 服务器结构保持一致
     *
     * @param ftpClient              :连接成功有效的 FTP客户端连接
     * @param absoluteLocalDirectory :本地存储文件的绝对路径,如 E:\gxg\ftpDownload
     * @param relativeRemotePath     :ftpFile 文件在服务器所在的绝对路径,此方法强制路径使用右斜杠"\",如 "\video\2018.mp4"
     * @return
     */
   public static void downloadSingleFile(FTPClient ftpClient, String absoluteLocalDirectory, String relativeRemotePath) {
        /**如果 FTP 连接已经关闭,或者连接无效,则直接返回*/
        if (!ftpClient.isConnected() || !ftpClient.isAvailable()) {
            System.out.println(">>>>>FTP服务器连接已经关闭或者连接无效*********");
            return;
        }
        if (StringUtils.isBlank(absoluteLocalDirectory) || StringUtils.isBlank(relativeRemotePath)) {
            System.out.println(">>>>>下载时遇到本地存储路径或者ftp服务器文件路径为空,放弃...*********");
            return;
        }
        try {
            /**没有对应路径时,FTPFile[] 大小为0,不会为null*/
            FTPFile[] ftpFiles = ftpClient.listFiles(relativeRemotePath);
            FTPFile ftpFile = null;
            if (ftpFiles.length >= 1) {
                ftpFile = ftpFiles[0];
            }
            if (ftpFile != null && ftpFile.isFile()) {
                /** ftpFile.getName():获取的是文件名称,如 123.mp4
                 * 必须保证文件存放的父目录必须存在,否则 retrieveFile 保存文件时报错
                 */
                File localFile = new File(absoluteLocalDirectory, relativeRemotePath);
                if (!localFile.getParentFile().exists()) {
                    localFile.getParentFile().mkdirs();
                }
                OutputStream outputStream = new FileOutputStream(localFile);
                String workDir = relativeRemotePath.substring(0, relativeRemotePath.lastIndexOf("\\"));
                if (StringUtils.isBlank(workDir)) {
                    workDir = "/";
                }
                /**文件下载前,FTPClient工作目录必须切换到文件所在的目录,否则下载失败
                 * "/" 表示用户根目录*/
                ftpClient.changeWorkingDirectory(workDir);
                /**下载指定的 FTP 文件 到本地
                 * 1)注意只能是文件,不能直接下载整个目录
                 * 2)如果文件本地已经存在,默认会重新下载
                 * 3)下载文件之前,ftpClient 工作目录必须是下载文件所在的目录
                 * 4)下载成功返回 true,失败返回 false
                 */
                ftpClient.retrieveFile(ftpFile.getName(), outputStream);

                outputStream.flush();
                outputStream.close();
                System.out.println(">>>>>FTP服务器文件下载完毕*********" + ftpFile.getName());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        System.out.println("-----------------------应用启动------------------------");
        FTPClient ftpClient = FTPUtil.connectFtpServer("192.168.1.20", 21, "ftpChina", "ftpChina123456", "gbk");
        downloadSingleFile(ftpClient, "E:\\gxg\\ftpDownload", "\\1.png");
        downloadSingleFile(ftpClient, "E:\\gxg\\ftpDownload", "\\xml\\schedule.xml");
        closeFTPConnect(ftpClient);
        System.out.println("-----------------------应用关闭------------------------");
    }

控制台输出:

-----------------------应用启动------------------------
>>>>>FTP服务器文件下载完毕*********1.png
>>>>>FTP服务器文件下载完毕*********schedule.xml
-----------------------应用关闭------------------------

Process finished with exit code 0

文件目录下载

  • 下载整个目录,包括所有子孙目录下的文件,实质也是遍历目录下的文件逐个下载,然后调用上面下载单个文件的方法进行下载

    /**
     * 遍历 FTP 服务器指定目录下的所有文件(包含子孙文件)
     *
     * @param ftpClient        :连接成功有效的 FTP客户端连接
     * @param remotePath       :查询的 FTP 服务器目录,如果文件,则视为无效,使用绝对路径,如"/"、"/video"、"\\"、"\\video"
     * @param relativePathList :返回查询结果,其中为服务器目录下的文件相对路径,如:\1.png、\docs\overview-tree.html 等
     * @return
     */
    public static List<String> loopServerPath(FTPClient ftpClient, String remotePath, List<String> relativePathList) {
        /**如果 FTP 连接已经关闭,或者连接无效,则直接返回*/
        if (!ftpClient.isConnected() || !ftpClient.isAvailable()) {
            System.out.println("ftp 连接已经关闭或者连接无效......");
            return relativePathList;
        }
        try {
            /**转移到FTP服务器根目录下的指定子目录
             * 1)"/":表示用户的根目录,为空时表示不变更
             * 2)参数必须是目录,当是文件时改变路径无效
             * */
            ftpClient.changeWorkingDirectory(remotePath);
            /** listFiles:获取FtpClient连接的当前下的一级文件列表(包括子目录)
             * 1)FTPFile[] ftpFiles = ftpClient.listFiles("/docs/info");
             *      获取服务器指定目录下的子文件列表(包括子目录),以 FTP 登录用户的根目录为基准,与 FTPClient 当前连接目录无关
             * 2)FTPFile[] ftpFiles = ftpClient.listFiles("/docs/info/springmvc.txt");
             *      获取服务器指定文件,此时如果文件存在时,则 FTPFile[] 大小为 1,就是此文件
             * */
            FTPFile[] ftpFiles = ftpClient.listFiles();
            if (ftpFiles != null && ftpFiles.length > 0) {
                for (FTPFile ftpFile : ftpFiles) {
                    if (ftpFile.isFile()) {
                        String relativeRemotePath = remotePath + "\\" + ftpFile.getName();
                        relativePathList.add(relativeRemotePath);
                    } else {
                        loopServerPath(ftpClient, remotePath + "\\" + ftpFile.getName(), relativePathList);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return relativePathList;
    }

    public static void main(String[] args) throws Exception {

        System.out.println("-----------------------应用启动------------------------");
        FTPClient ftpClient = FTPUtil.connectFtpServer("192.168.1.20", 21, "ftpChina", "ftpChina123456", "gbk");

        List<String> relativePathList = new ArrayList<>();

        relativePathList = loopServerPath(ftpClient, "\\video", relativePathList);
        for (String relativePath : relativePathList) {
            System.out.println("准备下载的服务器文件:" + relativePath);
            downloadSingleFile(ftpClient, "E:\\gxg\\ftpDownload", relativePath);
        }

      /*  downloadSingleFile(ftpClient, "E:\\gxg\\ftpDownload", "\\1.png");
        downloadSingleFile(ftpClient, "E:\\gxg\\ftpDownload", "\\xml\\schedule.xml");*/

        closeFTPConnect(ftpClient);
        System.out.println("-----------------------应用关闭------------------------");
    }

控制台输出:

-----------------------应用启动------------------------
准备下载的服务器文件:\video\2.mp4
>>>>>FTP服务器文件下载完毕*********2.mp4
准备下载的服务器文件:\video\3.mp4
>>>>>FTP服务器文件下载完毕*********3.mp4
准备下载的服务器文件:\video\4.mp4
>>>>>FTP服务器文件下载完毕*********4.mp4
准备下载的服务器文件:\video\5.mp4
>>>>>FTP服务器文件下载完毕*********5.mp4
准备下载的服务器文件:\video\6.mp4
>>>>>FTP服务器文件下载完毕*********6.mp4
准备下载的服务器文件:\video\7.mp4
>>>>>FTP服务器文件下载完毕*********7.mp4
准备下载的服务器文件:\video\8.mp4
>>>>>FTP服务器文件下载完毕*********8.mp4
准备下载的服务器文件:\video\fff.mp4
>>>>>FTP服务器文件下载完毕*********fff.mp4
准备下载的服务器文件:\video\logS\2018-07-10 - 副本 - 副本.log
>>>>>FTP服务器文件下载完毕*********2018-07-10 - 副本 - 副本.log
准备下载的服务器文件:\video\logS\2018-07-10.log
>>>>>FTP服务器文件下载完毕*********2018-07-10.log
准备下载的服务器文件:\video\logS\2018-07-17 - 副本 (2).log
>>>>>FTP服务器文件下载完毕*********2018-07-17 - 副本 (2).log
准备下载的服务器文件:\video\logS\2018-07-17 - 副本.log
>>>>>FTP服务器文件下载完毕*********2018-07-17 - 副本.log
准备下载的服务器文件:\video\logS\2018-07-17.log
>>>>>FTP服务器文件下载完毕*********2018-07-17.log
-----------------------应用关闭------------------------

Process finished with exit code 0

上传文件/目录

  • 上传文件时可以直接使用 API 上传,上传目录时只能遍历文件逐个上传

    /**
     * 上传本地文件 或 目录 至 FTP 服务器----保持 FTP 服务器与本地 文件目录结构一致
     *
     * @param ftpClient  连接成功有效的 FTPClinet
     * @param uploadFile 待上传的文件 或 文件夹(此时会遍历逐个上传)
     * @throws Exception
     */
    public static void uploadFiles(FTPClient ftpClient, File uploadFile) {
        /**如果 FTP 连接已经关闭,或者连接无效,则直接返回*/
        if (!ftpClient.isConnected() || !ftpClient.isAvailable()) {
            System.out.println(">>>>>FTP服务器连接已经关闭或者连接无效*****放弃文件上传****");
            return;
        }
        if (uploadFile == null && !uploadFile.exists()) {
            System.out.println(">>>>>待上传文件为空或者文件不存在*****放弃文件上传****");
            return;
        }
        try {
            if (uploadFile.isDirectory()) {
                /**如果被上传的是目录时
                 * makeDirectory:在 FTP 上创建目录(方法执行完,服务器就会创建好目录,如果目录本身已经存在,则不会再创建)
                 * 1)可以是相对路径,即不以"/"开头,相对的是 FTPClient 当前的工作路径,如 "video"、"视频" 等,会在当前工作目录进行新建目录
                 * 2)可以是绝对路径,即以"/"开头,与 FTPCLient 当前工作目录无关,如 "/images"、"/images/2018"
                 * 3)注意多级目录时,必须确保父目录存在,否则创建失败,
                 *      如 "video/201808"、"/images/2018" ,如果 父目录 video与images不存在,则创建失败
                 * */
                ftpClient.makeDirectory(uploadFile.getName());
                /**变更 FTPClient 工作目录到新目录
                 * 1)不以"/"开头表示相对路径,新目录以当前工作目录为基准,即当前工作目录下不存在此新目录时,变更失败
                 * 2)参数必须是目录,当是文件时改变路径无效*/
                ftpClient.changeWorkingDirectory(uploadFile.getName());

                File[] listFiles = uploadFile.listFiles();
                for (int i = 0; i < listFiles.length; i++) {
                    File loopFile = listFiles[i];
                    if (loopFile.isDirectory()) {
                        /**如果有子目录,则迭代调用方法进行上传*/
                        uploadFiles(ftpClient, loopFile);
                        /**changeToParentDirectory:将 FTPClient 工作目录移到上一层
                         * 这一步细节很关键,子目录上传完成后,必须将工作目录返回上一层,否则容易导致文件上传后,目录不一致
                         * */
                        ftpClient.changeToParentDirectory();
                    } else {
                        /**如果目录中全是文件,则直接上传*/
                        FileInputStream input = new FileInputStream(loopFile);
                        ftpClient.storeFile(loopFile.getName(), input);
                        input.close();
                        System.out.println(">>>>>文件上传成功****" + loopFile.getPath());
                    }
                }
            } else {
                /**如果被上传的是文件时*/
                FileInputStream input = new FileInputStream(uploadFile);
                /** storeFile:将本地文件上传到服务器
                 * 1)如果服务器已经存在此文件,则不会重新覆盖,即不会再重新上传
                 * 2)如果当前连接FTP服务器的用户没有写入的权限,则不会上传成功,但是也不会报错抛异常
                 * */
                ftpClient.storeFile(uploadFile.getName(), input);
                input.close();
                System.out.println(">>>>>文件上传成功****" + uploadFile.getPath());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {

        System.out.println("-----------------------应用启动------------------------");
        FTPClient ftpClient = FTPUtil.connectFtpServer("192.168.1.20", 21, "ftpChina", "ftpChina123456", "gbk");

        uploadFiles(ftpClient, new File("E:\\gxg\\datas"));

        closeFTPConnect(ftpClient);
        System.out.println("-----------------------应用关闭------------------------");
    }

控制台输出:

-----------------------应用启动------------------------
>>>>>文件上传成功****E:\gxg\datas\新建 Microsoft Word 文档.docx
>>>>>文件上传成功****E:\gxg\datas\新建文件夹\新建 RTF 文档.rtf
>>>>>文件上传成功****E:\gxg\datas\表A——30 概要设计说明书审批表(监理审核表是否保留).doc
-----------------------应用关闭------------------------

Process finished with exit code 0

文件同步

  • 保持本地文件与 FTP 服务器文件内容一致,本地客户端以 FTP 服务器为准。

    /**
     * 同步本地目录与 FTP 服务器目录
     * 1)约定:FTP 服务器有,而本地没有的,则下载下来;本地有,而ftp服务器没有的,则将本地多余的删除
     * 2)始终确保本地与 ftp 服务器内容一致
     * 2)让 FTP 服务器与 本地目录保持结构一致,如 服务器上是 /docs/overview-tree.html,则本地也是 localDir/docs/overview-tree.html
     *
     * @param ftpClient        连接成功有效的 FTPClinet
     * @param localSyncFileDir :与 FTP 目录进行同步的本地目录
     */
    public static void syncLocalDir(FTPClient ftpClient, String localSyncFileDir) throws IOException {
        /**如果 FTP 连接已经关闭,或者连接无效,则直接返回*/
        if (!ftpClient.isConnected() || !ftpClient.isAvailable() || StringUtils.isBlank(localSyncFileDir)) {
            System.out.println(">>>>>FTP服务器连接已经关闭或者连接无效*********");
            return;
        }

        /** 获取本地存储目录下的文件*/
        Collection<File> fileCollection = FileWmxUtil.localListFiles(new File(localSyncFileDir));
        System.out.println(">>>>>本地存储目录共有文件数量*********" + fileCollection.size());

        /**获取 FTP 服务器下的相对路径*/
        List<String> relativePathList = new ArrayList<>();
        relativePathList = loopServerPath(ftpClient, "", relativePathList);
        System.out.println(">>>>>FTP 服务器端共有文件数量*********" + relativePathList.size());

        /**
         * 遍历 本地存储目录下的文件
         * 1)如果本地文件在 FTP 服务器上不存在,则删除它
         * 2)如果本地文件在 FTP 服务器上存在,则比较两种大小
         *    如果大小不一致,则重新下载
         */
        for (File localFile : fileCollection) {
            String localFilePath = localFile.getPath();
            String localFileSuffi = localFilePath.replace(localSyncFileDir, "");
            if (relativePathList.contains(localFileSuffi)) {
                /**本地此文件在 FTP 服务器存在
                 * 1)比较大小,如果本地文件与服务器文件大小一致,则跳过
                 * 2)如果大小不一致,则删除本地文件,重新下载
                 * 3)最后都要删除relativePathList中的此元素,减轻后一次循环的压力*/
                FTPFile[] ftpFiles = ftpClient.listFiles(localFileSuffi);
                System.out.println(">>>>>本地文件 在 FTP 服务器已存在*********" + localFile.getPath());
                if (ftpFiles.length >= 1 && localFile.length() != ftpFiles[0].getSize()) {
                    downloadSingleFile(ftpClient, localSyncFileDir, localFileSuffi);
                    System.out.println(">>>>>本地文件与 FTP 服务器文件大小不一致,重新下载*********" + localFile.getPath());
                }
                relativePathList.remove(localFileSuffi);
            } else {
                System.out.println(">>>>>本地文件在 FTP 服务器不存在,删除本地文件*********" + localFile.getPath());
                /**本地此文件在 FTP 服务器不存在
                 * 1)删除本地文件
                 * 2)如果当前文件所在目录下文件已经为空,则将此父目录也一并删除*/
                localFile.delete();
                File parentFile = localFile.getParentFile();
                while (parentFile.list().length == 0) {
                    parentFile.delete();
                    parentFile = parentFile.getParentFile();
                }
            }
        }
        for (int i = 0; i < relativePathList.size(); i++) {
            System.out.println(">>>>> FTP 服务器存在新文件,准备下载*********" + relativePathList.get(i));
            downloadSingleFile(ftpClient, localSyncFileDir, relativePathList.get(i));
        }
    }

    public static void main(String[] args) throws Exception {

        System.out.println("-----------------------应用启动------------------------");
        FTPClient ftpClient = FTPUtil.connectFtpServer("192.168.1.20", 21, "ftpChina", "ftpChina123456", "gbk");

        syncLocalDir(ftpClient,"E:\\gxg\\ftpDownload");

        closeFTPConnect(ftpClient);
        System.out.println("-----------------------应用关闭------------------------");
    }

控制台输出:

-----------------------应用启动------------------------
>>>>>本地存储目录共有文件数量*********15
>>>>>FTP 服务器端共有文件数量*********24
>>>>>本地文件 在 FTP 服务器已存在*********E:\gxg\ftpDownload\1.png
>>>>>本地文件 在 FTP 服务器已存在*********E:\gxg\ftpDownload\video\2.mp4
>>>>>本地文件 在 FTP 服务器已存在*********E:\gxg\ftpDownload\video\3.mp4
>>>>>本地文件 在 FTP 服务器已存在*********E:\gxg\ftpDownload\video\4.mp4
>>>>>本地文件 在 FTP 服务器已存在*********E:\gxg\ftpDownload\video\5.mp4
>>>>>本地文件 在 FTP 服务器已存在*********E:\gxg\ftpDownload\video\6.mp4
>>>>>本地文件 在 FTP 服务器已存在*********E:\gxg\ftpDownload\video\7.mp4
>>>>>本地文件 在 FTP 服务器已存在*********E:\gxg\ftpDownload\video\8.mp4
>>>>>本地文件 在 FTP 服务器已存在*********E:\gxg\ftpDownload\video\fff.mp4
>>>>>本地文件 在 FTP 服务器已存在*********E:\gxg\ftpDownload\video\logS\2018-07-10 - 副本 - 副本.log
>>>>>本地文件 在 FTP 服务器已存在*********E:\gxg\ftpDownload\video\logS\2018-07-10.log
>>>>>本地文件 在 FTP 服务器已存在*********E:\gxg\ftpDownload\video\logS\2018-07-17 - 副本 (2).log
>>>>>本地文件 在 FTP 服务器已存在*********E:\gxg\ftpDownload\video\logS\2018-07-17 - 副本.log
>>>>>本地文件 在 FTP 服务器已存在*********E:\gxg\ftpDownload\video\logS\2018-07-17.log
>>>>>本地文件 在 FTP 服务器已存在*********E:\gxg\ftpDownload\xml\schedule.xml
>>>>> FTP 服务器存在新文件,准备下载*********\2018-06-04.log
>>>>>FTP服务器文件下载完毕*********2018-06-04.log
>>>>> FTP 服务器存在新文件,准备下载*********\act_2018-05-30-19-52-57.xml
>>>>>FTP服务器文件下载完毕*********act_2018-05-30-19-52-57.xml
>>>>> FTP 服务器存在新文件,准备下载*********\act_2018-05-30-20-12-13.xml
>>>>>FTP服务器文件下载完毕*********act_2018-05-30-20-12-13.xml
>>>>> FTP 服务器存在新文件,准备下载*********\act_2018-05-30-20-25-35 - 副本.xml
>>>>>FTP服务器文件下载完毕*********act_2018-05-30-20-25-35 - 副本.xml
>>>>> FTP 服务器存在新文件,准备下载*********\act_2018-05-30-20-25-35.xml
>>>>>FTP服务器文件下载完毕*********act_2018-05-30-20-25-35.xml
>>>>> FTP 服务器存在新文件,准备下载*********\AxInterop.AcroPDFLib.dll
>>>>>FTP服务器文件下载完毕*********AxInterop.AcroPDFLib.dll
>>>>> FTP 服务器存在新文件,准备下载*********\datas\新建 Microsoft Word 文档.docx
>>>>>FTP服务器文件下载完毕*********新建 Microsoft Word 文档.docx
>>>>> FTP 服务器存在新文件,准备下载*********\datas\新建文件夹\新建 RTF 文档.rtf
>>>>>FTP服务器文件下载完毕*********新建 RTF 文档.rtf
>>>>> FTP 服务器存在新文件,准备下载*********\datas\表A——30 概要设计说明书审批表(监理审核表是否保留).doc
>>>>>FTP服务器文件下载完毕*********表A——30 概要设计说明书审批表(监理审核表是否保留).doc
-----------------------应用关闭------------------------

Process finished with exit code 0

  • 上例中依赖一个获取本地目录下所有子孙内容的方法,用的是 commons-io 中的方法:
package www.wmx.com.util;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.TrueFileFilter;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;

/**
 * Created by Administrator on 2018/8/30 0030.
 * 自定义文件工具类
 */
public class FileWmxUtil {

    public static void main(String[] args) {
        File fileDir = new File("E:\\gxg\\resources\\docs");
    }

    /**
     * 遍历目录下的所有文件--方式1
     *
     * @param targetDir
     */
    public static Collection<File> localListFiles(File targetDir) {
        Collection<File> fileCollection = new ArrayList<>();
        if (targetDir != null && targetDir.exists() && targetDir.isDirectory()) {
            /**
             * targetDir:不要为 null、不要是文件、不要不存在
             * 第二个 文件过滤 参数如果为 FalseFileFilter.FALSE ,则不会查询任何文件
             * 第三个 目录过滤 参数如果为 FalseFileFilter.FALSE , 则只获取目标文件夹下的一级文件,而不会迭代获取子文件夹下的文件
             */
            fileCollection = FileUtils.listFiles(targetDir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE);
        }
        return fileCollection;
    }
}

删除文件/目录

  • 如果登录账号拥有写的权限,则可以上传文件、修改文件、删除文件。
  • 当是目录时会迭代进行删除

    /**
     * 删除服务器的文件
     *
     * @param ftpClient   连接成功且有效的 FTP客户端
     * @param deleteFiles 待删除的文件或者目录,为目录时,会逐个删除,
     *                    路径必须是绝对路径,如 "/1.png"、"/video/3.mp4"、"/images/2018"
     *                    "/" 表示用户根目录,则删除所有内容
     */
    public static void deleteServerFiles(FTPClient ftpClient, String deleteFiles) {
        /**如果 FTP 连接已经关闭,或者连接无效,则直接返回*/
        if (!ftpClient.isConnected() || !ftpClient.isAvailable()) {
            System.out.println(">>>>>FTP服务器连接已经关闭或者连接无效*****放弃文件上传****");
            return;
        }
        try {
            /** 尝试改变当前工作目录到 deleteFiles
             * 1)changeWorkingDirectory:变更FTPClient当前工作目录,变更成功返回true,否则失败返回false
             * 2)如果变更工作目录成功,则表示 deleteFiles 为服务器已经存在的目录
             * 3)否则变更失败,则认为 deleteFiles 是文件,是文件时则直接删除
             */
            boolean changeFlag = ftpClient.changeWorkingDirectory(deleteFiles);
            if (changeFlag) {
                /**当被删除的是目录时*/
                FTPFile[] ftpFiles = ftpClient.listFiles();
                for (FTPFile ftpFile : ftpFiles) {
                    System.out.println("----------------::::" + ftpClient.printWorkingDirectory());
                    if (ftpFile.isFile()) {
                        boolean deleteFlag = ftpClient.deleteFile(ftpFile.getName());
                        if (deleteFlag) {
                            System.out.println(">>>>>删除服务器文件成功****" + ftpFile.getName());
                        } else {
                            System.out.println(">>>>>删除服务器文件失败****" + ftpFile.getName());
                        }
                    } else {
                        /**printWorkingDirectory:获取 FTPClient 客户端当前工作目录
                         * 然后开始迭代删除子目录
                         */
                        String workingDirectory = ftpClient.printWorkingDirectory();
                        deleteServerFiles(ftpClient, workingDirectory + "/" + ftpFile.getName());
                    }
                }
                /**printWorkingDirectory:获取 FTPClient 客户端当前工作目录
                 * removeDirectory:删除FTP服务端的空目录,注意如果目录下存在子文件或者子目录,则删除失败
                 * 运行到这里表示目录下的内容已经删除完毕,此时再删除当前的为空的目录,同时将工作目录移动到上移层级
                 * */
                String workingDirectory = ftpClient.printWorkingDirectory();
                ftpClient.removeDirectory(workingDirectory);
                ftpClient.changeToParentDirectory();
            } else {
                /**deleteFile:删除FTP服务器上的文件
                 * 1)只用于删除文件而不是目录,删除成功时,返回 true
                 * 2)删除目录时无效,方法返回 false
                 * 3)待删除文件不存在时,删除失败,返回 false
                 * */
                boolean deleteFlag = ftpClient.deleteFile(deleteFiles);
                if (deleteFlag) {
                    System.out.println(">>>>>删除服务器文件成功****" + deleteFiles);
                } else {
                    System.out.println(">>>>>删除服务器文件失败****" + deleteFiles);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {

        System.out.println("-----------------------应用启动------------------------");
        FTPClient ftpClient = FTPUtil.connectFtpServer("192.168.1.20", 21, "ftpChina", "ftpChina123456", "gbk");
        deleteServerFiles(ftpClient,"/datas");
        closeFTPConnect(ftpClient);
        System.out.println("-----------------------应用关闭------------------------");
    }

控制台输出:

-----------------------应用启动------------------------
----------------::::/datas
>>>>>删除服务器文件成功****新建 Microsoft Word 文档.docx
----------------::::/datas
----------------::::/datas/新建文件夹
>>>>>删除服务器文件成功****新建 RTF 文档.rtf
----------------::::/datas
>>>>>删除服务器文件成功****表A——30 概要设计说明书审批表(监理审核表是否保留).doc
-----------------------应用关闭------------------------

Process finished with exit code 0

完整类文件

FTPUtil

package www.wmx.com.util;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

import java.io.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.ResourceBundle;

/**
 * Created by Administrator on 2018/8/29 0029.
 * FTP 工具类
 */
public class FTPUtil {
    /**
     * 连接 FTP 服务器
     *
     * @param addr     FTP 服务器 IP 地址
     * @param port     FTP 服务器端口号
     * @param username 登录用户名
     * @param password 登录密码
     * @return
     * @throws Exception
     */
    public static FTPClient connectFtpServer(String addr, int port, String username, String password, String controlEncoding) {
        FTPClient ftpClient = new FTPClient();
        try {
            /**设置文件传输的编码*/
            ftpClient.setControlEncoding(controlEncoding);

            /**连接 FTP 服务器
             * 如果连接失败,则此时抛出异常,如ftp服务器服务关闭时,抛出异常:
             * java.net.ConnectException: Connection refused: connect*/
            ftpClient.connect(addr, port);
            /**登录 FTP 服务器
             * 1)如果传入的账号为空,则使用匿名登录,此时账号使用 "Anonymous",密码为空即可*/
            if (StringUtils.isBlank(username)) {
                ftpClient.login("Anonymous", "");
            } else {
                ftpClient.login(username, password);
            }

            /** 设置传输的文件类型
             * BINARY_FILE_TYPE:二进制文件类型
             * ASCII_FILE_TYPE:ASCII传输方式,这是默认的方式
             * ....
             */
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);

            /**
             * 确认应答状态码是否正确完成响应
             * 凡是 2开头的 isPositiveCompletion 都会返回 true,因为它底层判断是:
             * return (reply >= 200 && reply < 300);
             */
            int reply = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                /**
                 * 如果 FTP 服务器响应错误 中断传输、断开连接
                 * abort:中断文件正在进行的文件传输,成功时返回 true,否则返回 false
                 * disconnect:断开与服务器的连接,并恢复默认参数值
                 */
                ftpClient.abort();
                ftpClient.disconnect();
            } else {
            }
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println(">>>>>FTP服务器连接登录失败,请检查连接参数是否正确,或者网络是否通畅*********");
        }
        return ftpClient;
    }

    /**
     * 下载 FTP 服务器上指定的单个文件,而且本地存放的文件相对部分路径 会与 FTP 服务器结构保持一致
     *
     * @param ftpClient              :连接成功有效的 FTP客户端连接
     * @param absoluteLocalDirectory :本地存储文件的绝对路径,如 E:\gxg\ftpDownload
     * @param relativeRemotePath     :ftpFile 文件在服务器所在的绝对路径,此方法强制路径使用右斜杠"\",如 "\video\2018.mp4"
     * @return
     */
    public static void downloadSingleFile(FTPClient ftpClient, String absoluteLocalDirectory, String relativeRemotePath) {
        /**如果 FTP 连接已经关闭,或者连接无效,则直接返回*/
        if (!ftpClient.isConnected() || !ftpClient.isAvailable()) {
            System.out.println(">>>>>FTP服务器连接已经关闭或者连接无效*********");
            return;
        }
        if (StringUtils.isBlank(absoluteLocalDirectory) || StringUtils.isBlank(relativeRemotePath)) {
            System.out.println(">>>>>下载时遇到本地存储路径或者ftp服务器文件路径为空,放弃...*********");
            return;
        }
        try {
            /**没有对应路径时,FTPFile[] 大小为0,不会为null*/
            FTPFile[] ftpFiles = ftpClient.listFiles(relativeRemotePath);
            FTPFile ftpFile = null;
            if (ftpFiles.length >= 1) {
                ftpFile = ftpFiles[0];
            }
            if (ftpFile != null && ftpFile.isFile()) {
                /** ftpFile.getName():获取的是文件名称,如 123.mp4
                 * 必须保证文件存放的父目录必须存在,否则 retrieveFile 保存文件时报错
                 */
                File localFile = new File(absoluteLocalDirectory, relativeRemotePath);
                if (!localFile.getParentFile().exists()) {
                    localFile.getParentFile().mkdirs();
                }

                OutputStream outputStream = new FileOutputStream(localFile);

                String workDir = relativeRemotePath.substring(0, relativeRemotePath.lastIndexOf("\\"));
                if (StringUtils.isBlank(workDir)) {
                    workDir = "/";
                }
                /**文件下载前,FTPClient工作目录必须切换到文件所在的目录,否则下载失败
                 * "/" 表示用户根目录*/
                ftpClient.changeWorkingDirectory(workDir);
                /**下载指定的 FTP 文件 到本地
                 * 1)注意只能是文件,不能直接下载整个目录
                 * 2)如果文件本地已经存在,默认会重新下载
                 * 3)下载文件之前,ftpClient 工作目录必须是下载文件所在的目录
                 * 4)下载成功返回 true,失败返回 false
                 */
                ftpClient.retrieveFile(ftpFile.getName(), outputStream);

                outputStream.flush();
                outputStream.close();
                System.out.println(">>>>>FTP服务器文件下载完毕*********" + ftpFile.getName());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 遍历 FTP 服务器指定目录下的所有文件(包含子孙文件)
     *
     * @param ftpClient        :连接成功有效的 FTP客户端连接
     * @param remotePath       :查询的 FTP 服务器目录,如果文件,则视为无效,使用绝对路径,如"/"、"/video"、"\\"、"\\video"
     * @param relativePathList :返回查询结果,其中为服务器目录下的文件相对路径,如:\1.png、\docs\overview-tree.html 等
     * @return
     */
    public static List<String> loopServerPath(FTPClient ftpClient, String remotePath, List<String> relativePathList) {
        /**如果 FTP 连接已经关闭,或者连接无效,则直接返回*/
        if (!ftpClient.isConnected() || !ftpClient.isAvailable()) {
            System.out.println("ftp 连接已经关闭或者连接无效......");
            return relativePathList;
        }
        try {
            /**转移到FTP服务器根目录下的指定子目录
             * 1)"/":表示用户的根目录,为空时表示不变更
             * 2)参数必须是目录,当是文件时改变路径无效
             * */
            ftpClient.changeWorkingDirectory(remotePath);
            /** listFiles:获取FtpClient连接的当前下的一级文件列表(包括子目录)
             * 1)FTPFile[] ftpFiles = ftpClient.listFiles("/docs/info");
             *      获取服务器指定目录下的子文件列表(包括子目录),以 FTP 登录用户的根目录为基准,与 FTPClient 当前连接目录无关
             * 2)FTPFile[] ftpFiles = ftpClient.listFiles("/docs/info/springmvc.txt");
             *      获取服务器指定文件,此时如果文件存在时,则 FTPFile[] 大小为 1,就是此文件
             * */
            FTPFile[] ftpFiles = ftpClient.listFiles();
            if (ftpFiles != null && ftpFiles.length > 0) {
                for (FTPFile ftpFile : ftpFiles) {
                    if (ftpFile.isFile()) {
                        String relativeRemotePath = remotePath + "\\" + ftpFile.getName();
                        relativePathList.add(relativeRemotePath);
                    } else {
                        loopServerPath(ftpClient, remotePath + "\\" + ftpFile.getName(), relativePathList);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return relativePathList;
    }

    /**
     * 同步本地目录与 FTP 服务器目录
     * 1)约定:FTP 服务器有,而本地没有的,则下载下来;本地有,而ftp服务器没有的,则将本地多余的删除
     * 2)始终确保本地与 ftp 服务器内容一致
     * 2)让 FTP 服务器与 本地目录保持结构一致,如 服务器上是 /docs/overview-tree.html,则本地也是 localDir/docs/overview-tree.html
     *
     * @param ftpClient        连接成功有效的 FTPClinet
     * @param localSyncFileDir :与 FTP 目录进行同步的本地目录
     */
    public static void syncLocalDir(FTPClient ftpClient, String localSyncFileDir) throws IOException {
        /**如果 FTP 连接已经关闭,或者连接无效,则直接返回*/
        if (!ftpClient.isConnected() || !ftpClient.isAvailable() || StringUtils.isBlank(localSyncFileDir)) {
            System.out.println(">>>>>FTP服务器连接已经关闭或者连接无效*********");
            return;
        }

        /** 获取本地存储目录下的文件*/
        Collection<File> fileCollection = FileWmxUtil.localListFiles(new File(localSyncFileDir));
        System.out.println(">>>>>本地存储目录共有文件数量*********" + fileCollection.size());

        /**获取 FTP 服务器下的相对路径*/
        List<String> relativePathList = new ArrayList<>();
        relativePathList = loopServerPath(ftpClient, "", relativePathList);
        System.out.println(">>>>>FTP 服务器端共有文件数量*********" + relativePathList.size());

        /**
         * 遍历 本地存储目录下的文件
         * 1)如果本地文件在 FTP 服务器上不存在,则删除它
         * 2)如果本地文件在 FTP 服务器上存在,则比较两种大小
         *    如果大小不一致,则重新下载
         */
        for (File localFile : fileCollection) {
            String localFilePath = localFile.getPath();
            String localFileSuffi = localFilePath.replace(localSyncFileDir, "");
            if (relativePathList.contains(localFileSuffi)) {
                /**本地此文件在 FTP 服务器存在
                 * 1)比较大小,如果本地文件与服务器文件大小一致,则跳过
                 * 2)如果大小不一致,则删除本地文件,重新下载
                 * 3)最后都要删除relativePathList中的此元素,减轻后一次循环的压力*/
                FTPFile[] ftpFiles = ftpClient.listFiles(localFileSuffi);
                System.out.println(">>>>>本地文件 在 FTP 服务器已存在*********" + localFile.getPath());
                if (ftpFiles.length >= 1 && localFile.length() != ftpFiles[0].getSize()) {
                    downloadSingleFile(ftpClient, localSyncFileDir, localFileSuffi);
                    System.out.println(">>>>>本地文件与 FTP 服务器文件大小不一致,重新下载*********" + localFile.getPath());
                }
                relativePathList.remove(localFileSuffi);
            } else {
                System.out.println(">>>>>本地文件在 FTP 服务器不存在,删除本地文件*********" + localFile.getPath());
                /**本地此文件在 FTP 服务器不存在
                 * 1)删除本地文件
                 * 2)如果当前文件所在目录下文件已经为空,则将此父目录也一并删除*/
                localFile.delete();
                File parentFile = localFile.getParentFile();
                while (parentFile.list().length == 0) {
                    parentFile.delete();
                    parentFile = parentFile.getParentFile();
                }
            }
        }
        for (int i = 0; i < relativePathList.size(); i++) {
            System.out.println(">>>>> FTP 服务器存在新文件,准备下载*********" + relativePathList.get(i));
            downloadSingleFile(ftpClient, localSyncFileDir, relativePathList.get(i));
        }
    }

    /**
     * 上传本地文件 或 目录 至 FTP 服务器----保持 FTP 服务器与本地 文件目录结构一致
     *
     * @param ftpClient  连接成功有效的 FTPClinet
     * @param uploadFile 待上传的文件 或 文件夹(此时会遍历逐个上传)
     * @throws Exception
     */
    public static void uploadFiles(FTPClient ftpClient, File uploadFile) {
        /**如果 FTP 连接已经关闭,或者连接无效,则直接返回*/
        if (!ftpClient.isConnected() || !ftpClient.isAvailable()) {
            System.out.println(">>>>>FTP服务器连接已经关闭或者连接无效*****放弃文件上传****");
            return;
        }
        if (uploadFile == null && !uploadFile.exists()) {
            System.out.println(">>>>>待上传文件为空或者文件不存在*****放弃文件上传****");
            return;
        }
        try {
            if (uploadFile.isDirectory()) {
                /**如果被上传的是目录时
                 * makeDirectory:在 FTP 上创建目录(方法执行完,服务器就会创建好目录,如果目录本身已经存在,则不会再创建)
                 * 1)可以是相对路径,即不以"/"开头,相对的是 FTPClient 当前的工作路径,如 "video"、"视频" 等,会在当前工作目录进行新建目录
                 * 2)可以是绝对路径,即以"/"开头,与 FTPCLient 当前工作目录无关,如 "/images"、"/images/2018"
                 * 3)注意多级目录时,必须确保父目录存在,否则创建失败,
                 *      如 "video/201808"、"/images/2018" ,如果 父目录 video与images不存在,则创建失败
                 * */
                ftpClient.makeDirectory(uploadFile.getName());
                /**变更 FTPClient 工作目录到新目录
                 * 1)不以"/"开头表示相对路径,新目录以当前工作目录为基准,即当前工作目录下不存在此新目录时,变更失败
                 * 2)参数必须是目录,当是文件时改变路径无效*/
                ftpClient.changeWorkingDirectory(uploadFile.getName());

                File[] listFiles = uploadFile.listFiles();
                for (int i = 0; i < listFiles.length; i++) {
                    File loopFile = listFiles[i];
                    if (loopFile.isDirectory()) {
                        /**如果有子目录,则迭代调用方法进行上传*/
                        uploadFiles(ftpClient, loopFile);
                        /**changeToParentDirectory:将 FTPClient 工作目录移到上一层
                         * 这一步细节很关键,子目录上传完成后,必须将工作目录返回上一层,否则容易导致文件上传后,目录不一致
                         * */
                        ftpClient.changeToParentDirectory();
                    } else {
                        /**如果目录中全是文件,则直接上传*/
                        FileInputStream input = new FileInputStream(loopFile);
                        ftpClient.storeFile(loopFile.getName(), input);
                        input.close();
                        System.out.println(">>>>>文件上传成功****" + loopFile.getPath());
                    }
                }
            } else {
                /**如果被上传的是文件时*/
                FileInputStream input = new FileInputStream(uploadFile);
                /** storeFile:将本地文件上传到服务器
                 * 1)如果服务器已经存在此文件,则不会重新覆盖,即不会再重新上传
                 * 2)如果当前连接FTP服务器的用户没有写入的权限,则不会上传成功,但是也不会报错抛异常
                 * */
                ftpClient.storeFile(uploadFile.getName(), input);
                input.close();
                System.out.println(">>>>>文件上传成功****" + uploadFile.getPath());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除服务器的文件
     *
     * @param ftpClient   连接成功且有效的 FTP客户端
     * @param deleteFiles 待删除的文件或者目录,为目录时,会逐个删除,
     *                    路径必须是绝对路径,如 "/1.png"、"/video/3.mp4"、"/images/2018"
     *                    "/" 表示用户根目录,则删除所有内容
     */
    public static void deleteServerFiles(FTPClient ftpClient, String deleteFiles) {
        /**如果 FTP 连接已经关闭,或者连接无效,则直接返回*/
        if (!ftpClient.isConnected() || !ftpClient.isAvailable()) {
            System.out.println(">>>>>FTP服务器连接已经关闭或者连接无效*****放弃文件上传****");
            return;
        }
        try {
            /** 尝试改变当前工作目录到 deleteFiles
             * 1)changeWorkingDirectory:变更FTPClient当前工作目录,变更成功返回true,否则失败返回false
             * 2)如果变更工作目录成功,则表示 deleteFiles 为服务器已经存在的目录
             * 3)否则变更失败,则认为 deleteFiles 是文件,是文件时则直接删除
             */
            boolean changeFlag = ftpClient.changeWorkingDirectory(deleteFiles);
            if (changeFlag) {
                /**当被删除的是目录时*/
                FTPFile[] ftpFiles = ftpClient.listFiles();
                for (FTPFile ftpFile : ftpFiles) {
                    System.out.println("----------------::::" + ftpClient.printWorkingDirectory());
                    if (ftpFile.isFile()) {
                        boolean deleteFlag = ftpClient.deleteFile(ftpFile.getName());
                        if (deleteFlag) {
                            System.out.println(">>>>>删除服务器文件成功****" + ftpFile.getName());
                        } else {
                            System.out.println(">>>>>删除服务器文件失败****" + ftpFile.getName());
                        }
                    } else {
                        /**printWorkingDirectory:获取 FTPClient 客户端当前工作目录
                         * 然后开始迭代删除子目录
                         */
                        String workingDirectory = ftpClient.printWorkingDirectory();
                        deleteServerFiles(ftpClient, workingDirectory + "/" + ftpFile.getName());
                    }
                }
                /**printWorkingDirectory:获取 FTPClient 客户端当前工作目录
                 * removeDirectory:删除FTP服务端的空目录,注意如果目录下存在子文件或者子目录,则删除失败
                 * 运行到这里表示目录下的内容已经删除完毕,此时再删除当前的为空的目录,同时将工作目录移动到上移层级
                 * */
                String workingDirectory = ftpClient.printWorkingDirectory();
                ftpClient.removeDirectory(workingDirectory);
                ftpClient.changeToParentDirectory();
            } else {
                /**deleteFile:删除FTP服务器上的文件
                 * 1)只用于删除文件而不是目录,删除成功时,返回 true
                 * 2)删除目录时无效,方法返回 false
                 * 3)待删除文件不存在时,删除失败,返回 false
                 * */
                boolean deleteFlag = ftpClient.deleteFile(deleteFiles);
                if (deleteFlag) {
                    System.out.println(">>>>>删除服务器文件成功****" + deleteFiles);
                } else {
                    System.out.println(">>>>>删除服务器文件失败****" + deleteFiles);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 使用完毕,应该及时关闭连接
     * 终止 ftp 传输
     * 断开 ftp 连接
     *
     * @param ftpClient
     * @return
     */
    public static FTPClient closeFTPConnect(FTPClient ftpClient) {
        try {
            if (ftpClient != null && ftpClient.isConnected()) {
                ftpClient.abort();
                ftpClient.disconnect();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ftpClient;
    }

    public static void main(String[] args) throws Exception {

        System.out.println("-----------------------应用启动------------------------");
        FTPClient ftpClient = FTPUtil.connectFtpServer("192.168.1.20", 21, "ftpChina", "ftpChina123456", "gbk");
        deleteServerFiles(ftpClient,"/datas");
        closeFTPConnect(ftpClient);
        System.out.println("-----------------------应用关闭------------------------");
    }
}

FileWmxUtil

package www.wmx.com.util;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.TrueFileFilter;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;

/**
 * Created by Administrator on 2018/8/30 0030.
 * 自定义文件工具类
 */
public class FileWmxUtil {

    public static void main(String[] args) {
        File fileDir = new File("E:\\gxg\\resources\\docs");
    }

    /**
     * 遍历目录下的所有文件--方式1
     *
     * @param targetDir
     */
    public static Collection<File> localListFiles(File targetDir) {
        Collection<File> fileCollection = new ArrayList<>();
        if (targetDir != null && targetDir.exists() && targetDir.isDirectory()) {
            /**
             * targetDir:不要为 null、不要是文件、不要不存在
             * 第二个 文件过滤 参数如果为 FalseFileFilter.FALSE ,则不会查询任何文件
             * 第三个 目录过滤 参数如果为 FalseFileFilter.FALSE , 则只获取目标文件夹下的一级文件,而不会迭代获取子文件夹下的文件
             */
            fileCollection = FileUtils.listFiles(targetDir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE);
        }
        return fileCollection;
    }
}

猜你喜欢

转载自blog.csdn.net/wangmx1993328/article/details/82150290
今日推荐