apache common net storeFile 返回 550 或 425 之类的错误
retrieveFileStream
和 storeFile
这两个方法用的其实是 同一个 FTP 客户端连接,而 retrieveFileStream
返回的 InputStream
并没有被正确关闭,导致连接仍然处于“下载”状态。当你紧接着调用
storeFile
时,FTP 连接已经“忙”了,无法立刻切换到“上传”模式,于是上传失败。根本原因
-
retrieveFileStream
打开了一个数据连接用于下载,返回的InputStream
需要 显式地读完并关闭,否则 FTP 客户端不会发送COMPLETE_TRANSFER
指令,连接不会复位。 -
storeFile
需要一个新的数据连接,但旧的还没释放,于是服务器返回550
或425
之类的错误,导致storeFile
返回false
。
正确做法
-
确保下载流完全消费并关闭
用IOUtils.closeQuietly
或 try-with-resources 把InputStream
关干净。 -
调用
completePendingCommand()
这是 FTPClient 的“复位”操作,告诉服务器传输已完成。 -
检查 FTP 的传输模式(主动/被动)
有些服务器在两种模式之间切换时会丢连接,建议在下载和上传前都设置一次enterLocalPassiveMode()
。
示例代码
java
FTPClient ftp = new FTPClient();
ftp.connect("host");
ftp.login("user", "pass");
ftp.enterLocalPassiveMode(); // 推荐
ftp.setFileType(FTP.BINARY_FILE_TYPE);
// 1. 下载
try (InputStream is = ftp.retrieveFileStream(remotePath)) {
// 处理流
IOUtils.copy(is, System.out); // 举例
}
// 2. 关键:通知服务器传输结束
ftp.completePendingCommand();
// 3. 上传
try (InputStream newIs = new FileInputStream(localFile)) {
boolean success = ftp.storeFile(remotePath, newIs);
if (!success) {
throw new IOException("上传失败,服务器返回:" + ftp.getReplyString());
}
}
常见坑
-
忘了
completePendingCommand()
:这是 FTPClient 的“潜规则”,不调用就卡状态。 -
多次复用同一个
InputStream
:下载流不能复用,必须重新打开。 -
服务器限制:部分 FTP 服务器禁止“下载后立刻上传”,需要断开重连。
总结
一句话:下载后必须
不关闭/不复位,连接状态就乱了,上传自然失败。
completePendingCommand()
,再上传。不关闭/不复位,连接状态就乱了,上传自然失败。