curl可正常上传至IONOS FTPS,但Apache Commons FTPSClient实现失败求助
解决Apache Commons FTPSClient连接IONOS HiDrive FTPS上传失败的问题
结合你用curl --ftp-ssl -T test.txt ftp://username:password@ftp.hidrive.ionos.com/public/test.txt成功的情况,你的Java代码需要补充以下关键配置,才能和curl的行为对齐:
1. 采用显式FTPS模式(放弃隐式模式)
IONOS HiDrive的FTPS服务默认使用显式模式(端口21),你设置isImplicit=true时用的是隐式FTPS(默认端口990),这会导致连接被服务器直接重置。必须初始化FTPSClient为显式模式:
FTPSClient ftpsClient = new FTPSClient(false); // false对应显式FTPS,和curl的--ftp-ssl行为一致
2. 强制启用数据通道加密(执行PBSZ和PROT命令)
curl默认会自动发送PBSZ 0和PROT P命令加密数据传输通道,但Apache Commons FTPSClient不会默认执行这两步。服务器拒绝PORT/PASV操作,核心原因就是数据通道未加密,权限校验不通过。登录成功后必须添加这两行:
ftpsClient.execPBSZ(0); // 保护缓冲区大小设为0(PROT为P时的强制要求) ftpsClient.execPROT("P"); // 将数据通道设置为私有加密模式
3. 正确配置被动模式与文件类型
- 开启本地被动模式:
ftpsClient.enterLocalPassiveMode();,和curl默认的被动模式行为一致,适配服务器的防火墙策略。 - 设置文件传输类型为二进制:
ftpsClient.setFileType(FTP.BINARY_FILE_TYPE);,避免ASCII转换导致的文件损坏或上传失败。
完整可运行代码示例
import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPReply; import org.apache.commons.net.ftp.FTPSClient; import java.io.FileInputStream; import java.io.IOException; public class HiDriveFTPSUpload { public static void main(String[] args) { String server = "ftp.hidrive.ionos.com"; int port = 21; String username = "your_username"; String password = "your_password"; String localFilePath = "test.txt"; String remoteFilePath = "/public/test.txt"; FTPSClient ftpsClient = new FTPSClient(false); try { // 连接服务器 ftpsClient.connect(server, port); int replyCode = ftpsClient.getReplyCode(); if (!FTPReply.isPositiveCompletion(replyCode)) { throw new IOException("服务器连接失败,回复码:" + replyCode); } // 登录 if (!ftpsClient.login(username, password)) { throw new IOException("登录失败,服务器回复:" + ftpsClient.getReplyString()); } // 启用数据通道加密(关键步骤) ftpsClient.execPBSZ(0); if (!ftpsClient.execPROT("P")) { throw new IOException("设置数据通道加密失败:" + ftpsClient.getReplyString()); } // 配置被动模式与文件类型 ftpsClient.enterLocalPassiveMode(); ftpsClient.setFileType(FTP.BINARY_FILE_TYPE); // 上传文件 try (FileInputStream inputStream = new FileInputStream(localFilePath)) { boolean uploadSuccess = ftpsClient.storeFile(remoteFilePath, inputStream); if (uploadSuccess) { System.out.println("文件上传成功"); } else { throw new IOException("上传失败,服务器回复:" + ftpsClient.getReplyString()); } } // 退出登录并断开连接 ftpsClient.logout(); } catch (IOException e) { e.printStackTrace(); } finally { if (ftpsClient.isConnected()) { try { ftpsClient.disconnect(); } catch (IOException e) { e.printStackTrace(); } } } } }
额外注意事项
- 若遇到证书校验问题(测试环境可临时忽略),可以给FTPSClient设置自定义TrustManager,但生产环境建议导入服务器的CA证书进行合法校验。
- 确保远程路径
/public/test.txt的权限正确(curl能成功则权限无问题,核心还是代码配置遗漏)。
内容的提问来源于stack exchange,提问作者Markus S.
相关产品推荐
相关产品推荐

