上一篇
Apache Commons Net库的FTPClient类,先连接登录
FTP服务器,设置被动模式与二进制类型,再用storeFile方法上传文件
Java中实现FTP文件上传主要依赖Apache Commons Net库提供的FTPClient类,以下是详细的实现步骤、代码示例及注意事项:
核心实现流程
-
建立连接与登录认证
- 使用
FTPClient.connect(hostname, port)建立TCP连接,参数包括服务器地址和端口(默认21),若采用被动模式需调用enterLocalPassiveMode()解决防火墙拦截问题。 - 通过
login(username, password)进行身份验证,建议添加回复码校验确保登录成功(如检查是否返回正完成状态码)。
- 使用
-
配置传输参数

- 设置二进制传输模式:
setFileType(FTP.BINARY_FILE_TYPE)保证所有类型文件完整传输,避免文本模式导致的格式损坏。 - 处理中文路径编码:当涉及非ASCII字符时,需将本地编码转换为ISO-8859-1标准,例如使用
new String(s.getBytes("GBK"), StandardCharsets.ISO_8859_1)实现编码转换。
- 设置二进制传输模式:
-
目录结构处理
- 自动创建缺失目录:遍历目标路径的各个层级,若当前目录不存在则调用
makeDirectory()逐级创建,可结合changeWorkingDirectory()判断是否需要新建文件夹。 - 路径拼接规范:建议使用作为分隔符统一处理不同操作系统下的路径差异。
- 自动创建缺失目录:遍历目标路径的各个层级,若当前目录不存在则调用
-
执行文件上传

- 获取本地文件输入流:通过
FileInputStream读取待上传文件内容,对于大文件建议使用缓冲流提升性能。 - 调用
storeFile(remotePath, inputStream)执行上传操作,其中remotePath应包含完整目标路径及文件名。
- 获取本地文件输入流:通过
-
资源释放与清理
- 严格关闭输入流防止内存泄漏,按顺序执行注销(
logout())和断开连接(disconnect())操作,建议使用try-with-resources语法管理资源。
- 严格关闭输入流防止内存泄漏,按顺序执行注销(
完整代码示例
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import java.io.;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class AdvancedFtpUploader {
public static void main(String[] args) throws Exception {
// 配置参数
String server = "ftp.example.com";
int port = 21;
String user = "your_username";
String passwd = "your_password";
String localPath = "/data/reports/quarterly.pdf";
String remoteDir = "/backups/financial/2025Q3/";
// 初始化客户端并连接
FTPClient client = new FTPClient();
try {
client.connect(server, port);
if (!FTPReply.isPositiveCompletion(client.getReplyCode())) {
throw new IllegalStateException("连接失败: " + client.getReplyString());
}
// 设置中文支持与被动模式
client.setControlEncoding("GBK");
client.enterLocalPassiveMode();
client.setFileType(FTPClient.BINARY_FILE_TYPE);
// 登录验证
client.login(user, passwd);
verifyLoginSuccess(client);
// 准备上传数据源
File srcFile = new File(localPath);
try (InputStream dataIn = new BufferedInputStream(new FileInputStream(srcFile))) {
// 构造目标全路径并上传
String targetPath = buildValidRemotePath(remoteDir, srcFile.getName());
boolean success = client.storeFile(targetPath, dataIn);
if (success) {
System.out.println("上传成功至:" + targetPath);
} else {
System.err.println("上传失败,错误码:" + client.getReplyCode());
}
} finally {
safeDisconnect(client);
}
} catch (IOException e) {
System.err.println("FTP操作异常: " + e.getMessage());
safeDisconnect(client);
}
}
private static void verifyLoginSuccess(FTPClient client) throws IOException {
if (!FTPReply.isPositivePreliminary(client.getReplyCode())) {
throw new SecurityException("认证失败: " + client.getReplyString());
}
}
private static String buildValidRemotePath(String baseDir, String filename) {
return baseDir + new String(filename.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
}
private static void safeDisconnect(FTPClient client) {
try {
if (client.isConnected()) {
client.logout();
client.disconnect();
}
} catch (IOException e) {
System.err.println("断开连接时发生错误: " + e.getMessage());
}
}
}
关键机制说明表
| 功能模块 | 实现方式 | 作用描述 |
|---|---|---|
| 编码转换 | new String(original.getBytes(srcCharset), tgtCharset) |
确保多字节字符集安全传输 |
| 被动模式启用 | enterLocalPassiveMode() |
突破NAT/防火墙限制,增强兼容性 |
| 目录自动创建 | 递归调用changeWorkingDirectory()配合makeDirectory() |
动态构建服务器端存储结构 |
| 异常处理 | 分层捕获IO异常与协议错误,区分网络层和应用层故障 | 提高程序健壮性 |
| 资源管理 | try-with-resources结合显式关闭连接 | 防止句柄泄漏 |
常见问题解决方案
- 乱码问题:强制指定控制通道编码为GBK,并对文件名进行ISO-8859-1转码,特别注意Windows系统默认使用ANSI页码集的情况。
- 大文件传输中断:建议添加进度监控回调,同时设置合理的超时参数(如
client.setDataTimeout(30000))。 - 并发冲突:同一用户多次上传时可能出现”文件锁定”错误,可通过随机后缀重命名机制规避。
FAQs
Q1: 为什么上传中文文件名会出现乱码?如何修复?
A: FTP协议底层仅支持ISO-8859-1编码,直接传输UTF-8中文会导致截断,解决方案是在设置控制编码为GBK后,将文件名按如下方式转换:new String(filename.getBytes("GBK"), StandardCharsets.ISO_8859_1),该操作会保留原始字节序的同时符合协议规范。

Q2: 遇到“550 No such file or directory”错误提示怎么办?
A: 此错误通常由两个原因导致:①目标路径不存在;②权限不足,应首先检查远程路径是否存在,若不存在需调用makeDirectory()创建,其次确认所用账号对目标目录具有写入权限,可通过ftpClient.execute("SITE CHMOD 777 /target/path")临时
