Java实现FTP/SFTP调用实现

1. Maven依赖

<dependency>
    <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.9.0</version>
</dependency>

2. 基本连接与登录

import org.apache.commons.net.ftp.FTPClient;

FTPClient ftp = new FTPClient();
ftp.connect("ftp.example.com", 21); // 默认端口21
boolean login = ftp.login("username", "password");
if (login) {
    System.out.println("登录成功");
} else {
    System.out.println("登录失败");
}

3. 设置文件类型和编码

ftp.setFileType(FTPClient.BINARY_FILE_TYPE); // 二进制文件
ftp.setControlEncoding("UTF-8"); // 避免中文乱码

4. 被动/主动模式切换

ftp.enterLocalPassiveMode(); // 推荐被动模式,穿透防火墙方便
// ftp.enterLocalActiveMode(); // 主动模式,不推荐

5. 上传文件

import java.io.FileInputStream;

try (FileInputStream fis = new FileInputStream("localfile.txt")) {
    boolean done = ftp.storeFile("/remote/path/remote.txt", fis);
    System.out.println(done ? "上传成功" : "上传失败");
}

6. 下载文件

import java.io.FileOutputStream;

try (FileOutputStream fos = new FileOutputStream("localfile.txt")) {
    boolean done = ftp.retrieveFile("/remote/path/remote.txt", fos);
    System.out.println(done ? "下载成功" : "下载失败");
}

7. 列出目录和文件

String[] files = ftp.listNames("/remote/path");
for (String name : files) {
    System.out.println(name);
}

8. 创建/删除目录与文件

ftp.makeDirectory("/remote/newdir");
ftp.deleteFile("/remote/path/file.txt");
ftp.removeDirectory("/remote/olddir");

9. 断点续传(下载)

import java.io.RandomAccessFile;
import java.io.OutputStream;

long remoteSize = ftp.mlistFile("/remote/path/remote.txt").getSize();
long localSize = new File("localfile.txt").length();
if (localSize < remoteSize) {
    try (RandomAccessFile raf = new RandomAccessFile("localfile.txt", "rw")) {
        raf.seek(localSize);
        ftp.setRestartOffset(localSize);
        try (OutputStream os = new FileOutputStream(raf.getFD())) {
            ftp.retrieveFile("/remote/path/remote.txt", os);
        }
    }
}

上传断点续传原理类似,需用 ftp.setRestartOffset(long) 配合 storeFile


10. 退出与资源释放

ftp.logout();
ftp.disconnect();

11. 常见异常处理

try {
    // FTP操作
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (ftp.isConnected()) {
        try {
            ftp.logout();
            ftp.disconnect();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

12. 典型完整代码示例

import org.apache.commons.net.ftp.FTPClient;
import java.io.*;

public class FtpDemo {
    public static void main(String[] args) {
        FTPClient ftp = new FTPClient();
        try {
            ftp.connect("ftp.example.com", 21);
            ftp.login("user", "pass");
            ftp.setControlEncoding("UTF-8");
            ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
            ftp.enterLocalPassiveMode();

            // 上传
            try (FileInputStream fis = new FileInputStream("local.txt")) {
                boolean ok = ftp.storeFile("/remote/remote.txt", fis);
                System.out.println("上传:" + ok);
            }

            // 下载
            try (FileOutputStream fos = new FileOutputStream("download.txt")) {
                boolean ok = ftp.retrieveFile("/remote/remote.txt", fos);
                System.out.println("下载:" + ok);
            }

            // 列目录
            for (String name : ftp.listNames("/remote")) {
                System.out.println("文件:" + name);
            }

            ftp.logout();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (ftp.isConnected()) {
                try {
                    ftp.disconnect();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

13. 进阶:FTP上传/下载进度监听

ftp.setCopyStreamListener(new CopyStreamListener() {
    @Override
    public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, long streamSize) {
        System.out.println("已传输:" + totalBytesTransferred + " 字节");
    }
    @Override
    public void bytesTransferred(CopyStreamEvent event) {}
});

14. 注意事项与建议

  • 优先用被动模式,防火墙友好。
  • 统一编码,避免中文乱码。
  • 及时关闭连接,防止资源泄漏。
  • FTP协议不安全,敏感场景推荐用 SFTP/FTPS。
  • 生产环境下可用连接池(如 Apache Commons Pool)管理 FTPClient 实例。

15. 递归上传/下载目录

15.1 递归上传目录

public static void uploadDirectory(FTPClient ftp, String remotePath, File localDir) throws IOException {
    ftp.makeDirectory(remotePath);
    File[] files = localDir.listFiles();
    if (files != null) {
        for (File file : files) {
            String remoteFilePath = remotePath + "/" + file.getName();
            if (file.isDirectory()) {
                uploadDirectory(ftp, remoteFilePath, file);
            } else {
                try (FileInputStream fis = new FileInputStream(file)) {
                    ftp.storeFile(remoteFilePath, fis);
                }
            }
        }
    }
}

15.2 递归下载目录

public static void downloadDirectory(FTPClient ftp, String remotePath, File localDir) throws IOException {
    FTPFile[] files = ftp.listFiles(remotePath);
    if (!localDir.exists()) localDir.mkdirs();
    for (FTPFile file : files) {
        String remoteFile = remotePath + "/" + file.getName();
        File localFile = new File(localDir, file.getName());
        if (file.isDirectory()) {
            downloadDirectory(ftp, remoteFile, localFile);
        } else {
            try (FileOutputStream fos = new FileOutputStream(localFile)) {
                ftp.retrieveFile(remoteFile, fos);
            }
        }
    }
}

16. 断点续传(上传)

public static boolean resumeUpload(FTPClient ftp, String remoteFilePath, File localFile) throws IOException {
    long remoteSize = 0;
    FTPFile[] files = ftp.listFiles(remoteFilePath);
    if (files.length == 1) remoteSize = files[0].getSize();
    try (RandomAccessFile raf = new RandomAccessFile(localFile, "r")) {
        raf.seek(remoteSize);
        ftp.setRestartOffset(remoteSize);
        try (InputStream is = new FileInputStream(localFile)) {
            is.skip(remoteSize);
            return ftp.appendFile(remoteFilePath, is);
        }
    }
}

17. FTP连接池管理(伪代码)

实际生产建议用连接池管理FTPClient实例,避免频繁创建销毁。

// 伪代码:可用Apache Commons Pool2实现
GenericObjectPool<FTPClient> pool = new GenericObjectPool<>(new FTPClientFactory());
FTPClient ftp = pool.borrowObject();
// ...操作
pool.returnObject(ftp);

具体实现可参考FTPClientPool开源项目或自定义工厂。


18. 常见异常与健壮性建议

  • 连接超时/断开:设置超时时间(ftp.setConnectTimeout/SoTimeout),操作前判断 isConnected()。
  • 传输中断:捕获 IOException,必要时自动重连并断点续传。
  • 编码不一致:确保 setControlEncoding 与服务器一致。
  • 资源泄漏:始终用 try-with-resources 或 finally 关闭流、断开连接。

19. SFTP实现(JSch库)

FTP协议不安全,很多企业推荐 SFTP(基于SSH)。常用库为 JSch:

Maven依赖:

<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.55</version>
</dependency>

基本用法:

import com.jcraft.jsch.*;

JSch jsch = new JSch();
Session session = jsch.getSession("user", "host", 22);
session.setPassword("password");
session.setConfig("StrictHostKeyChecking", "no");
session.connect();

ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp");
sftp.connect();

// 上传
sftp.put(new FileInputStream("local.txt"), "/remote/remote.txt");

// 下载
sftp.get("/remote/remote.txt", new FileOutputStream("local.txt"));

// 关闭
sftp.disconnect();
session.disconnect();

支持断点续传、递归操作,安全性高。


20. 实战经验与面试加分点

  • 自动重连与失败重试机制:遇到网络波动,自动断点续传、重连,提升健壮性。
  • 多线程并发上传/下载:可用线程池批量处理大批量文件。
  • 进度回调/监听:友好提示用户传输进度。
  • 安全合规:敏感业务优先用 SFTP/FTPS,FTP只用于非敏感场景。
  • 日志与监控:记录每次操作的成功/失败、耗时、异常,便于运维和审计。

21. 递归上传/下载目录

21.1 递归上传目录

public static void uploadDirectory(FTPClient ftp, String remotePath, File localDir) throws IOException {
    ftp.makeDirectory(remotePath);
    File[] files = localDir.listFiles();
    if (files != null) {
        for (File file : files) {
            String remoteFilePath = remotePath + "/" + file.getName();
            if (file.isDirectory()) {
                uploadDirectory(ftp, remoteFilePath, file);
            } else {
                try (FileInputStream fis = new FileInputStream(file)) {
                    ftp.storeFile(remoteFilePath, fis);
                }
            }
        }
    }
}

21.2 递归下载目录

public static void downloadDirectory(FTPClient ftp, String remotePath, File localDir) throws IOException {
    FTPFile[] files = ftp.listFiles(remotePath);
    if (!localDir.exists()) localDir.mkdirs();
    for (FTPFile file : files) {
        String remoteFile = remotePath + "/" + file.getName();
        File localFile = new File(localDir, file.getName());
        if (file.isDirectory()) {
            downloadDirectory(ftp, remoteFile, localFile);
        } else {
            try (FileOutputStream fos = new FileOutputStream(localFile)) {
                ftp.retrieveFile(remoteFile, fos);
            }
        }
    }
}

22. 断点续传(上传)

public static boolean resumeUpload(FTPClient ftp, String remoteFilePath, File localFile) throws IOException {
    long remoteSize = 0;
    FTPFile[] files = ftp.listFiles(remoteFilePath);
    if (files.length == 1) remoteSize = files[0].getSize();
    try (RandomAccessFile raf = new RandomAccessFile(localFile, "r")) {
        raf.seek(remoteSize);
        ftp.setRestartOffset(remoteSize);
        try (InputStream is = new FileInputStream(localFile)) {
            is.skip(remoteSize);
            return ftp.appendFile(remoteFilePath, is);
        }
    }
}

23. FTP连接池管理(伪代码)

实际生产建议用连接池管理FTPClient实例,避免频繁创建销毁。

// 伪代码:可用Apache Commons Pool2实现
GenericObjectPool<FTPClient> pool = new GenericObjectPool<>(new FTPClientFactory());
FTPClient ftp = pool.borrowObject();
// ...操作
pool.returnObject(ftp);

具体实现可参考FTPClientPool开源项目或自定义工厂。


24. 常见异常与健壮性建议

  • 连接超时/断开:设置超时时间(ftp.setConnectTimeout/SoTimeout),操作前判断 isConnected()。
  • 传输中断:捕获 IOException,必要时自动重连并断点续传。
  • 编码不一致:确保 setControlEncoding 与服务器一致。
  • 资源泄漏:始终用 try-with-resources 或 finally 关闭流、断开连接。

25. SFTP实现(JSch库)

FTP协议不安全,很多企业推荐 SFTP(基于SSH)。常用库为 JSch:

Maven依赖:

<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.55</version>
</dependency>

基本用法:

import com.jcraft.jsch.*;

JSch jsch = new JSch();
Session session = jsch.getSession("user", "host", 22);
session.setPassword("password");
session.setConfig("StrictHostKeyChecking", "no");
session.connect();

ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp");
sftp.connect();

// 上传
sftp.put(new FileInputStream("local.txt"), "/remote/remote.txt");

// 下载
sftp.get("/remote/remote.txt", new FileOutputStream("local.txt"));

// 关闭
sftp.disconnect();
session.disconnect();

支持断点续传、递归操作,安全性高。


26. 实战经验

  • 自动重连与失败重试机制:遇到网络波动,自动断点续传、重连,提升健壮性。
  • 多线程并发上传/下载:可用线程池批量处理大批量文件。
  • 进度回调/监听:友好提示用户传输进度。
  • 安全合规:敏感业务优先用 SFTP/FTPS,FTP只用于非敏感场景。
  • 日志与监控:记录每次操作的成功/失败、耗时、异常,便于运维和审计。

27. 多线程并发上传/下载

FTP多线程上传示例

ExecutorService executor = Executors.newFixedThreadPool(4);
List<File> filesToUpload = Arrays.asList(new File("a.txt"), new File("b.txt"));
for (File file : filesToUpload) {
    executor.submit(() -> {
        FTPClient ftp = new FTPClient();
        try {
            ftp.connect("host", 21);
            ftp.login("user", "pass");
            ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
            ftp.enterLocalPassiveMode();
            try (FileInputStream fis = new FileInputStream(file)) {
                ftp.storeFile("/remote/" + file.getName(), fis);
            }
            ftp.logout();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try { ftp.disconnect(); } catch (Exception ignored) {}
        }
    });
}
executor.shutdown();

实际生产建议配合连接池,避免频繁创建销毁FTPClient。


SFTP多线程下载示例(JSch)

ExecutorService executor = Executors.newFixedThreadPool(4);
List<String> filesToDownload = Arrays.asList("/remote/a.txt", "/remote/b.txt");
for (String remoteFile : filesToDownload) {
    executor.submit(() -> {
        JSch jsch = new JSch();
        try {
            Session session = jsch.getSession("user", "host", 22);
            session.setPassword("pass");
            session.setConfig("StrictHostKeyChecking", "no");
            session.connect();
            ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp");
            sftp.connect();
            try (FileOutputStream fos = new FileOutputStream(new File(new File(remoteFile).getName()))) {
                sftp.get(remoteFile, fos);
            }
            sftp.disconnect();
            session.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
    });
}
executor.shutdown();

28. 进度条与回调监听

FTP进度监听

ftp.setCopyStreamListener(new CopyStreamListener() {
    @Override
    public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, long streamSize) {
        int percent = (int)((double)totalBytesTransferred / streamSize * 100);
        System.out.println("进度:" + percent + "%");
    }
    @Override
    public void bytesTransferred(CopyStreamEvent event) {}
});

SFTP进度监听

sftp.get(remoteFile, localFilePath, new SftpProgressMonitor() {
    long transferred = 0;
    @Override
    public void init(int op, String src, String dest, long max) {}
    @Override
    public boolean count(long count) {
        transferred += count;
        System.out.println("已下载:" + transferred + "字节");
        return true;
    }
    @Override
    public void end() {
        System.out.println("下载完成");
    }
});

29. 异常自动重试与健壮性优化

int maxRetries = 3;
for (int i = 0; i < maxRetries; i++) {
    try {
        // FTP/SFTP操作
        break; // 成功则退出循环
    } catch (IOException e) {
        if (i == maxRetries - 1) throw e;
        System.out.println("重试第" + (i + 1) + "次");
        Thread.sleep(1000); // 间隔重试
    }
}

30. Spring集成FTP/SFTP(以SFTP为例)

application.yml配置:

sftp:
  host: sftp.example.com
  port: 22
  username: user
  password: pass

Java配置Bean:

@Configuration
public class SftpConfig {
    @Value("${sftp.host}") String host;
    @Value("${sftp.port}") int port;
    @Value("${sftp.username}") String username;
    @Value("${sftp.password}") String password;

    @Bean
    public ChannelSftp sftpChannel() throws Exception {
        JSch jsch = new JSch();
        Session session = jsch.getSession(username, host, port);
        session.setPassword(password);
        session.setConfig("StrictHostKeyChecking", "no");
        session.connect();
        ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp");
        sftp.connect();
        return sftp;
    }
}

实际生产建议用连接池或每次操作后断开连接。


31. 企业级 FTP/SFTP 实战技巧

31.1. 连接池管理(FTP/SFTP)

FTPClient 连接池(基于 Apache Commons Pool2)

使用连接池可以避免频繁创建和销毁连接,提升性能和并发能力。

Maven依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.11.1</version>
</dependency>

FTPClient工厂类示例:

public class FTPClientFactory extends BasePooledObjectFactory<FTPClient> {
    @Override
    public FTPClient create() throws Exception {
        FTPClient ftp = new FTPClient();
        ftp.connect("host", 21);
        ftp.login("user", "pass");
        ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
        ftp.enterLocalPassiveMode();
        return ftp;
    }
    @Override
    public PooledObject<FTPClient> wrap(FTPClient ftpClient) {
        return new DefaultPooledObject<>(ftpClient);
    }
    @Override
    public void destroyObject(PooledObject<FTPClient> p) throws Exception {
        FTPClient ftp = p.getObject();
        if (ftp.isConnected()) {
            ftp.logout();
            ftp.disconnect();
        }
    }
}

连接池使用:

GenericObjectPool<FTPClient> pool = new GenericObjectPool<>(new FTPClientFactory());
FTPClient ftp = pool.borrowObject();
// ...FTP操作
pool.returnObject(ftp);

SFTP连接池(JSch)

SFTP连接池实现类似,可以参考 spring-integration-sftp 或自定义 Session/ChannelSftp 池。


31.2. 批量任务调度与自动化

  • 使用 Quartz/Spring Task/定时器定期批量上传下载。
  • 支持任务失败自动重试、断点续传、日志记录。
  • 结合数据库或Redis保存任务状态,实现断点恢复和分布式调度。

31.3. Spring Boot 集成最佳实践

  • 配置参数通过 application.yml 管理,支持多环境切换。
  • 实现 @Service 层封装 FTP/SFTP 操作,业务代码只需调用方法,无需关心底层细节。
  • 配合连接池和异常处理,保证高并发和稳定性。

示例:

@Service
public class FtpService {
    @Autowired GenericObjectPool<FTPClient> ftpPool;

    public void upload(File file, String remotePath) throws Exception {
        FTPClient ftp = ftpPool.borrowObject();
        try (FileInputStream fis = new FileInputStream(file)) {
            ftp.storeFile(remotePath, fis);
        } finally {
            ftpPool.returnObject(ftp);
        }
    }
}

31.4. 性能优化建议

  • 连接池复用,减少连接建立/销毁开销。
  • 多线程并发,合理分配任务,提升吞吐量。
  • 断点续传,大文件/不稳定网络下提升成功率。
  • 进度监听与回调,提升用户体验。
  • 异常自动重试,增强健壮性。
  • 日志监控与报警,及时发现问题。

31.5. 常见故障排查

问题排查思路
连接超时检查网络、防火墙、端口设置、FTP/SFTP服务器状态
登录失败检查用户名密码、权限、IP白名单
文件传输失败检查路径、权限、磁盘空间、防火墙端口
中文乱码FTP设置setControlEncoding,SFTP使用UTF-8
断点续传异常检查服务器是否支持REST、offset,客户端逻辑是否正确
连接池泄漏检查借出归还逻辑,确保异常时也能归还连接

32. 面试加分点

  1. Java调用FTP/SFTP如何保证高并发和稳定性?
  2. 连接池管理、自动重试、断点续传、进度监听、资源自动释放。
  3. FTP和SFTP协议在Java实现上有哪些关键差异?
  4. FTP基于明文、双通道,SFTP基于SSH、单通道全加密,库和命令完全不同。
  5. 如何实现FTP/SFTP断点续传?
  6. FTP用setRestartOffset/REST命令,SFTP用seek/offset配合put/get。
  7. 如何防止中文乱码?
  8. FTP需设置setControlEncoding,SFTP默认UTF-8,必要时指定编码。
  9. 如何在Java中优雅处理FTP/SFTP的异常和资源释放?
  10. try-with-resources、finally块、连接池、日志记录、自动重试。
  11. 能清晰阐述 FTP/SFTP 协议区别及 Java 调用实现要点。
  12. 熟悉连接池设计原理及生产场景下的优化方案。
  13. 了解断点续传、进度监听、多线程并发、自动重试等高级特性。
  14. 掌握 Spring Boot 集成和企业级自动化任务调度实践。
  15. 能结合异常处理、日志监控、安全加固等运维经验给出解决方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猩火燎猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值