apache common net storeFile 返回 550 或 425 之类的错误

retrieveFileStreamstoreFile 这两个方法用的其实是 同一个 FTP 客户端连接,而 retrieveFileStream 返回的 InputStream 并没有被正确关闭,导致连接仍然处于“下载”状态。
当你紧接着调用 storeFile 时,FTP 连接已经“忙”了,无法立刻切换到“上传”模式,于是上传失败。

根本原因

  • retrieveFileStream 打开了一个数据连接用于下载,返回的 InputStream 需要 显式地读完并关闭,否则 FTP 客户端不会发送 COMPLETE_TRANSFER 指令,连接不会复位。
  • storeFile 需要一个新的数据连接,但旧的还没释放,于是服务器返回 550425 之类的错误,导致 storeFile 返回 false

正确做法

  1. 确保下载流完全消费并关闭
    IOUtils.closeQuietly 或 try-with-resources 把 InputStream 关干净。
  2. 调用 completePendingCommand()
    这是 FTPClient 的“复位”操作,告诉服务器传输已完成。
  3. 检查 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(),再上传。
不关闭/不复位,连接状态就乱了,上传自然失败。
posted @ 2025-08-07 18:27  牧之丨  阅读(17)  评论(0)    收藏  举报