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

被折叠的 条评论
为什么被折叠?



