杰瑞科技汇

Java FTP客户端代码如何实现?

使用 Apache Commons Net (最推荐)

这是最常用、最稳定、功能最全的 Java FTP 客户端库,它封装了复杂的 FTP 协议细节,提供了简单易用的 API。

添加依赖

您需要在您的项目中添加 commons-net 的依赖。

Maven (pom.xml):

<dependency>
    <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.9.0</version> <!-- 请使用最新版本 -->
</dependency>

Gradle (build.gradle):

implementation 'commons-net:commons-net:3.9.0' // 请使用最新版本

完整示例代码

这个示例展示了连接服务器、上传文件、下载文件、列出文件和断开连接等核心操作。

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FtpClientExample {
    private static final String SERVER = "ftp.example.com";
    private static final int PORT = 21;
    private static final String USER = "username";
    private static final String PASS = "password";
    public static void main(String[] args) {
        FTPClient ftpClient = new FTPClient();
        try {
            // 1. 连接到FTP服务器
            System.out.println("正在连接到 FTP 服务器: " + SERVER);
            ftpClient.connect(SERVER, PORT);
            int replyCode = ftpClient.getReplyCode();
            // 检查连接是否成功
            if (!FTPReply.isPositiveCompletion(replyCode)) {
                System.out.println("FTP 服务器拒绝连接。");
                return;
            }
            System.out.println("连接成功。");
            // 2. 登录
            if (ftpClient.login(USER, PASS)) {
                System.out.println("登录成功。");
            } else {
                System.out.println("登录失败。");
                return;
            }
            // 3. 设置文件传输模式为被动模式 (PASV)
            // 对于大多数网络环境(尤其是有防火墙的),被动模式是必需的
            ftpClient.enterLocalPassiveMode();
            // 4. 设置文件类型为二进制 (重要!)
            // 防止在传输文本文件时出现内容损坏
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            // --- 上传文件示例 ---
            String localFilePath = "C:/path/to/your/localfile.txt";
            String remoteFilePath = "uploadfile.txt";
            uploadFile(ftpClient, localFilePath, remoteFilePath);
            // --- 下载文件示例 ---
            String downloadLocalFilePath = "C:/path/to/your/downloadedfile.txt";
            String downloadRemoteFilePath = "uploadfile.txt"; // 下载刚才上传的文件
            downloadFile(ftpClient, downloadRemoteFilePath, downloadLocalFilePath);
            // --- 列出文件示例 ---
            listFiles(ftpClient);
        } catch (IOException e) {
            System.err.println("FTP 客户端发生错误: " + e.getMessage());
        } finally {
            // 5. 注销并断开连接
            try {
                if (ftpClient.isConnected()) {
                    ftpClient.logout();
                    ftpClient.disconnect();
                    System.out.println("已成功断开与服务器的连接。");
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 上传文件到FTP服务器
     */
    public static void uploadFile(FTPClient ftpClient, String localPath, String remotePath) throws IOException {
        try (FileInputStream fis = new FileInputStream(localPath)) {
            System.out.println("开始上传文件: " + localPath);
            boolean done = ftpClient.storeFile(remotePath, fis);
            if (done) {
                System.out.println("文件上传成功: " + remotePath);
            } else {
                System.out.println("文件上传失败。");
            }
        }
    }
    /**
     * 从FTP服务器下载文件
     */
    public static void downloadFile(FTPClient ftpClient, String remotePath, String localPath) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(localPath)) {
            System.out.println("开始下载文件: " + remotePath);
            boolean done = ftpClient.retrieveFile(remotePath, fos);
            if (done) {
                System.out.println("文件下载成功: " + localPath);
            } else {
                System.out.println("文件下载失败。");
            }
        }
    }
    /**
     * 列出FTP服务器上指定目录的文件
     */
    public static void listFiles(FTPClient ftpClient) throws IOException {
        System.out.println("列出当前目录下的文件:");
        String[] files = ftpClient.listNames();
        if (files != null && files.length > 0) {
            for (String file : files) {
                System.out.println(" - " + file);
            }
        } else {
            System.out.println("目录为空或无法列出文件。");
        }
    }
}

使用 Java 11+ 内置的 FTPClient

从 Java 11 开始,标准库中包含了一个实验性的 sun.net.ftp.FtpClient注意: 这个类位于 sun.net 包下,是非公开的 API,意味着它可能在未来的 Java 版本中被移除或修改,不推荐用于生产环境。

添加依赖 (无)

此类是 Java 标准库的一部分,无需额外依赖。

示例代码

import sun.net.ftp.FtpClient;
import sun.net.ftp.FtpProtocolException;
import java.io.*;
import java.net.InetAddress;
public class Java11FtpClientExample {
    private static final String SERVER = "ftp.example.com";
    private static final int PORT = 21;
    private static final String USER = "username";
    private static final String PASS = "password";
    public static void main(String[] args) {
        FtpClient ftpClient = null;
        try {
            // 1. 创建并连接FTP客户端
            ftpClient = new FtpClient();
            ftpClient.openServer(SERVER, PORT);
            System.out.println("连接成功。");
            // 2. 登录
            ftpClient.login(USER, PASS);
            System.out.println("登录成功。");
            // 3. 设置传输模式为二进制
            ftpClient.setBinaryType();
            // 4. 上传文件
            String localFilePath = "C:/path/to/your/localfile.txt";
            String remoteFilePath = "uploadfile_java11.txt";
            uploadFile(ftpClient, localFilePath, remoteFilePath);
            // 5. 下载文件
            String downloadLocalFilePath = "C:/path/to/your/downloadedfile_java11.txt";
            downloadFile(ftpClient, remoteFilePath, downloadLocalFilePath);
        } catch (IOException | FtpProtocolException e) {
            System.err.println("FTP 客户端发生错误: " + e.getMessage());
        } finally {
            // 6. 关闭连接
            if (ftpClient != null) {
                try {
                    ftpClient.closeServer();
                    System.out.println("已成功断开与服务器的连接。");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void uploadFile(FtpClient ftpClient, String localPath, String remotePath) throws IOException, FtpProtocolException {
        try (InputStream is = new FileInputStream(localPath)) {
            System.out.println("开始上传文件: " + localPath);
            ftpClient.putFile(remotePath, is);
            System.out.println("文件上传成功: " + remotePath);
        }
    }
    public static void downloadFile(FtpClient ftpClient, String remotePath, String localPath) throws IOException, FtpProtocolException {
        try (OutputStream os = new FileOutputStream(localPath)) {
            System.out.println("开始下载文件: " + remotePath);
            ftpClient.getFile(remotePath, os);
            System.out.println("文件下载成功: " + localPath);
        }
    }
}

手动实现 FTP 客户端 (仅用于学习)

警告: 这种方法非常复杂,容易出错,且难以处理所有 FTP 协议的边缘情况(如被动模式、不同服务器响应等。强烈不建议在生产环境中使用,此代码仅用于演示 FTP 协议的基本工作原理。

这里只展示一个非常简化的 LIST 命令实现,以帮助你理解底层通信。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class ManualFtpClient {
    private static final String SERVER = "ftp.example.com";
    private static final int PORT = 21;
    private static final String USER = "username";
    private static final String PASS = "password";
    public static void main(String[] args) {
        try (Socket socket = new Socket(SERVER, PORT);
             PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
             BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
            // 读取欢迎信息
            System.out.println("服务器响应: " + in.readLine());
            // 用户名
            out.println("USER " + USER);
            System.out.println("服务器响应: " + in.readLine());
            // 密码
            out.println("PASS " + PASS);
            System.out.println("服务器响应: " + in.readLine());
            // 切换到被动模式
            out.println("PASV");
            String pasvResponse = in.readLine();
            System.out.println("服务器响应 (PASV): " + pasvResponse);
            // --- 解析PASV响应以获取数据连接地址和端口 ---
            // PASV响应格式: "227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)"
            String[] parts = pasvResponse.split(",");
            int p1 = Integer.parseInt(parts[parts.length - 2]);
            int p2 = Integer.parseInt(parts[parts.length - 1].replace(")", ""));
            int dataPort = p1 * 256 + p2;
            // 从响应中提取IP地址 (简化处理,实际需要更复杂的解析)
            String ip = parts[1] + "." + parts[2] + "." + parts[3] + "." + parts[4];
            System.out.println("数据连接地址: " + ip + ":" + dataPort);
            // --- 建立数据连接 ---
            try (Socket dataSocket = new Socket(ip, dataPort);
                 BufferedReader dataIn = new BufferedReader(new InputStreamReader(dataSocket.getInputStream()))) {
                // 发送LIST命令
                out.println("LIST");
                System.out.println("服务器响应 (LIST): " + in.readLine()); // 150 Opening ASCII mode data connection for file list
                // 从数据连接中读取文件列表
                System.out.println("--- 文件列表 ---");
                String dataLine;
                while ((dataLine = dataIn.readLine()) != null) {
                    System.out.println(dataLine);
                }
                System.out.println("--- 文件列表结束 ---");
                System.out.println("服务器响应 (LIST完成): " + in.readLine()); // 226 Transfer complete.
            }
            // 退出
            out.println("QUIT");
            System.out.println("服务器响应 (QUIT): " + in.readLine());
        } catch (UnknownHostException e) {
            System.err.println("未知的主机: " + SERVER);
        } catch (IOException e) {
            System.err.println("I/O 错误: " + e.getMessage());
        }
    }
}

总结与建议

方法 优点 缺点 推荐场景
Apache Commons Net 功能强大、稳定可靠、文档齐全、社区活跃 需要添加第三方依赖 所有生产环境和严肃项目
Java 11+ FtpClient 无需外部依赖,是标准库的一部分 非公开API,有被移除风险,功能相对较少 快速原型开发或个人项目,且不介意使用非公开API
手动实现 能深入理解FTP协议 极其复杂、容易出错、功能有限、不健壮 学习和教学目的,绝不应该用于生产

对于任何实际应用,请务必选择 Apache Commons Net。 它是业界标准的解决方案,能为你节省大量的开发和调试时间。

分享:
扫描分享到社交APP
上一篇
下一篇