Java FTP 客户端失败,显示“425 无法建立连接”

Java FTP client failing with "425 Failed to establish connection"

当我尝试在 ftp 服务器上上传文件时,我的 FtpClient class 出现错误。我收到此消息:

220 (vsFTPd 3.0.3)
USER newftpuser
331 Please specify the password.
PASS ftp
230 Login successful.
TYPE I
200 Switching to Binary mode.
PORT 192,168,1,7,235,73
200 PORT command successful. Consider using PASV.
STOR /upload/logo.png
425 Failed to establish connection. 

FtpClient.java

import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

import java.io.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;

public class FtpClient {

    private String server;
    private int port;
    private String user;
    private String password;
    private FTPClient ftp;

    public void open() throws IOException {
        ftp = new FTPClient();
        ftp.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
        ftp.enterLocalPassiveMode();
        ftp.connect(server, port);
        int reply = ftp.getReplyCode();
        if (!FTPReply.isPositiveCompletion(reply)) {
            ftp.disconnect();
            throw new IOException("Exception in connecting to FTP Server");
        }
        ftp.login(user, password);

        ftp.setFileType(FTP.BINARY_FILE_TYPE);
    }

    public void close() throws IOException {
        ftp.disconnect();
    }

    public void putFileToPath(InputStream inputStream, String path) throws IOException {
        ftp.storeFile(path, inputStream);
    }
}

测试

@Test
public void dropFileOnFtpServer() throws IOException, URISyntaxException {

    ftpClient = new FtpClient(...);
    ftpClient.open();
    InputStream inputStream = this.getClass().getResourceAsStream("/images/logo.png");
    ftpClient.putFileToPath(inputStream, "/upload/logo.png");
    assertTrue(ftpClient.listFiles("/upload").contains("logo.png"));
    ftpClient.close();
}

已解决

问题的原因是被动模式。 appache.commons.net 的 FTPClient 在下载或上传文件之前需要手动启用被动模式,因此程序无法正常运行。

解决方案

public void putFileToPath(InputStream inputStream, String path) throws IOException { 
        ftp.enterLocalPassiveMode(); 
        /* I enable this mode before connecting ftp, so client doesnt work*/
        ftp.storeFile(path, inputStream);
} 

请检查 Android FTP error - 425 Can't open data connection and Error: 425 Can't open data connection 并根据这些指南编辑您的配置。

我正在使用这个 class 进行 ftp 处理,您可以实现自己的异常处理和配置管理。

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPSClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;

@Component
@Slf4j
public class FTPFileWriterImpl implements FTPFileWriter {
    private final FTPProperties ftpProperties;
    protected FTPClient ftpClient;

    @Autowired
    public FTPFileWriterImpl(@Autowired FTPProperties ftpProperties) {
        this.ftpProperties = ftpProperties;
    }

    //@PostConstruct
    public void init() {
        if (this.ftpProperties.isAutoStart()) {
            log.debug("Autostarting connection to FTP server.");
            this.open();
        }
    }

    public boolean open() {
        close();
        log.debug("Connecting and logging in to FTP server.");
        if (ftpProperties.getProtocolType().equals("SSL"))
            ftpClient = new FTPSClient();
        else
            ftpClient = new FTPClient();
        ftpClient.enterLocalPassiveMode();
        boolean loggedIn = false;
        try {
            ftpClient.connect(ftpProperties.getServer(), ftpProperties.getPort());
            loggedIn = ftpClient.login(ftpProperties.getUsername(), ftpProperties.getPassword());
            if (loggedIn) {
                ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
                if (ftpProperties.getKeepAliveTimout() > 0)
                    ftpClient.setControlKeepAliveTimeout(ftpProperties.getKeepAliveTimout());
            } else {
                log.error("Failed login to FTP server");
                ftpClient.logout();
                ftpClient.disconnect();
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return loggedIn;
    }

    public void close() {
        if (ftpClient != null) {
            try {
                if (ftpClient.isConnected()) {
                    ftpClient.logout();
                    ftpClient.disconnect();
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    public InputStream loadFile(String fileName) {
        try {
            log.debug("Trying to retrieve a file from remote path " + fileName);
            this.open();
            return ftpClient.retrieveFileStream(fileName);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new StorageFileNotFoundException("File not found!");
        } finally {
            this.close();
        }
    }

    public boolean delete(String fileName) throws IOException {
        this.open();
        boolean result = ftpClient.deleteFile(fileName);
        this.close();
        return result;
    }


    public String saveFile(InputStream inputStream, String dir, String fileName, boolean append) throws Exception {
        log.debug("Trying to store a file to destination path " + fileName);
        boolean result;
        this.open();
        try {
            String saveDir = this.ftpProperties.getRemoteDir() + "/" + dir;
            if (!directoryExists(saveDir)) {
                boolean makeDirResult = this.ftpClient.makeDirectory(saveDir);
                if(!makeDirResult) {
                    log.error("Storage directory {} does not exist on ftp server, failed to create it!", saveDir);
                    throw new StorageException("Storage directory {} does not exist on ftp server, failed to create it!");
                }
            }
            String remote = saveDir + "/" + fileName;

            if (append)
                result = ftpClient.appendFile(remote, inputStream);
            else
                result = ftpClient.storeFile(remote, inputStream);
            if (!result) {
                log.error("Cannot save file on server, Server response is: {}", ftpClient.getReplyCode());
                throw new StorageException(String.format("Cannot save file on server: %s", ftpClient.getReplyCode()));
            }
            return remote;
        }
        finally {
            this.close();
        }
    }

    public String saveFile(String sourcePath, String fileName, boolean append) throws Exception {
        InputStream inputStream;
        inputStream = new ClassPathResource(sourcePath).getInputStream();
        return this.saveFile(inputStream, "", fileName, append);
    }
    private boolean directoryExists(String dirPath) throws IOException {
        ftpClient.changeWorkingDirectory(dirPath);
        int returnCode = ftpClient.getReplyCode();
        return returnCode != 550;
    }
    public boolean isConnected() {
        boolean connected = false;
        if (ftpClient != null) {
            try {
                connected = ftpClient.sendNoOp();
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
        log.debug("Checking for connection to FTP server. Is connected: " + connected);
        return connected;
    }
}

FTPFileWriter接口:

import java.io.IOException;
import java.io.InputStream;

public interface FTPFileWriter {

    /**
     * Connects to a server and tries to log in the user.
     *
     * @return boolean True if successful, False otherwise.
     */
    boolean open();

    /**
     * Logouts the current user and disconnects from the server.
     */
    void close();

    /**
     * Retrieve a file from the ftp server.
     *
     * @param remotePath   Remote path for the file to retrieve.
     * @return boolean True if successful, False otherwise.
     */

    InputStream loadFile(String remotePath);

    boolean delete(String remotePath) throws IOException;

    /**
     * Store a file on the ftp server.
     *
     * @param inputStream Stream the new file is read from.
     * @param dir
     * @param destPath    Remote path the file should be placed at.
     * @param append      Append to an existing file or write as a new file.
     * @return boolean True if successful, False otherwise.
     */

    String saveFile(InputStream inputStream, String dir, String destPath, boolean append) throws Exception;

    /**
     * Store a file on the ftp server.
     *
     * @param sourcePath Local path the file is read from.
     * @param destPath   Remote path the file should be placed at.
     * @param append      Append to an existing file or write as a new file.
     * @return boolean True if successful, False otherwise.
     */

    String saveFile(String sourcePath, String destPath, boolean append) throws Exception;

    /**
     * Does a NOOP to see if the connection is valid.
     *
     * @return boolean True if connected, False otherwise.
     */

    boolean isConnected();
}

FTPProperties 是一个 class,它提供您的 ftp 设置:

public class FTPProperties {
    private String server;
    private String username;
    private String password;
    @Min(0)
    @Max(65535)
    private int port;
    private int keepAliveTimout;
    private boolean autoStart;
    private String protocolType;
    private String remoteDir;

    @PostConstruct
    public void init() {
        if (port == 0) {
            port = 21;
        }
    }
}

您可以实现自己的异常 classes 而不是 StorageExceptionStorageFileNotFoundException 或者抛出传统异常

如果你遇到 553 Could not create file 错误,它与 ftp 用户缺乏写入磁盘的权限有关,你可以通过 运行 在 [=35] 上执行命令来解决它=] ftp 主持人:

chown -R ftpusername /var/ftpfiles

将 /var/ftpfiles 替换为 ftp 存储文件夹的路径。