Java实现连接FTP服务并传递文件

简介: Java实现连接FTP服务并传递文件

public class FtpClientUtil {

private String host;

private int port;

private String username;

private String password;

private int bufferSize = 10 * 1024 * 1024;

private int soTimeout = 15000;

private FTPClient ftp;

public FTPClient getFtp() {
    return ftp;
}

public void setFtp(FTPClient ftp) {
    this.ftp = ftp;
}

private UploadStatus uploadStatus;

public UploadStatus getUploadStatus() {
    return uploadStatus;
}

public void setUploadStatus(UploadStatus uploadStatus) {
    this.uploadStatus = uploadStatus;
}

public static class Builder {
    private String host;
    private int port = 21;
    private String username;
    private String password;
    private int bufferSize = 1024 * 1024;
    private FTPClientConfig config;
    private int defaultTimeout = 15000;
    private int connectTimeout = 15000;
    private int dataTimeout = 15000;
    private int controlKeepAliveTimeout = 300;
    private int soTimeout = 15000;

    public Builder() {
    }

    public Builder host(String host) {
        this.host = host;
        return this;
    }

    public Builder port(int port) {
        this.port = port;
        return this;
    }

    public Builder username(String username) {
        this.username = username;
        return this;
    }

    public Builder password(String password) {
        this.password = password;
        return this;
    }

    public Builder bufferSize(int bufferSize) {
        this.bufferSize = bufferSize;
        return this;
    }

    public Builder config(FTPClientConfig config) {
        this.config = config;
        return this;
    }

    public Builder defaultTimeout(int defaultTimeout) {
        this.defaultTimeout = defaultTimeout;
        return this;
    }

    public Builder connectTimeout(int connectTimeout) {
        this.connectTimeout = connectTimeout;
        return this;
    }

    public Builder dataTimeout(int dataTimeout) {
        this.dataTimeout = dataTimeout;
        return this;
    }

    public Builder soTimeout(int soTimeout) {
        this.soTimeout = soTimeout;
        return this;
    }

    public Builder controlKeepAliveTimeout(int controlKeepAliveTimeout) {
        this.controlKeepAliveTimeout = controlKeepAliveTimeout;
        return this;
    }

    public FtpClientUtil build() throws IOException {
        FtpClientUtil instance = new FtpClientUtil(this.host, this.port, this.username, this.password,
                this.bufferSize, this.config, this.defaultTimeout, this.dataTimeout, this.connectTimeout,
                this.controlKeepAliveTimeout, this.soTimeout);
        return instance;
    }
}

private FtpClientUtil(String host, int port, String username, String password, int bufferSize,
        FTPClientConfig config, int defaultTimeout, int dataTimeout, int connectTimeout,
        int controlKeepAliveTimeout, int soTimeout) throws IOException {
    this.host = host;
    this.port = port;
    this.username = username;
    this.password = password;
    this.bufferSize = bufferSize;
    this.soTimeout = soTimeout;
    this.ftp = new FTPClient();
    if (config != null) {
        this.ftp.configure(config);
    }
    ftp.setControlEncoding("UTF-8");

// ftp.setControlEncoding("GBK");
// ftp.setControlEncoding("gb2312");

    ftp.enterLocalPassiveMode();
    ftp.setDefaultTimeout(defaultTimeout);
    ftp.setConnectTimeout(connectTimeout);
    ftp.setDataTimeout(dataTimeout);
    // ftp.setSendDataSocketBufferSize(1024 * 256);
    if (this.bufferSize > 0) {
        ftp.setBufferSize(this.bufferSize);
    }

    // keeping the control connection alive
    ftp.setControlKeepAliveTimeout(controlKeepAliveTimeout);// 每大约5分钟发一次noop,防止大文件传输导致的控制连接中断
}

public FtpClientUtil connect() throws SocketException, IOException {
    if (!this.ftp.isConnected()) {
        this.ftp.connect(this.host, this.port);
        int reply = this.ftp.getReplyCode();
        if (!FTPReply.isPositiveCompletion(reply)) {
            logger.warn("ftp服务器返回码[{}], 连接失败...", reply);
            throw new IllegalStateException("连接ftp服务器失败,返回的状态码是" + reply);
        }
    }
    this.ftp.setSoTimeout(this.soTimeout);
    return this;
}

public FtpClientUtil login() throws IOException {
    boolean suc = this.ftp.login(this.username, this.password);
    if (!suc) {
        throw new IllegalStateException("登录ftp服务器失败");
    }
    return this;
}

/**
 * ftp上传文件功能
 * 
 * @param file
 *            要上传的文件
 * @param relativePath
 *            要上传到ftp服务器的相对路径
 * @return
 * @throws IOException
 */
public FtpClientUtil upload(File file, String relativePath) throws IOException {
    FileInputStream fInputStream = new FileInputStream(file);
    return this.upload(fInputStream, file.getName(), relativePath, file.length());
}

public FtpClientUtil upload(InputStream inputStream, String name, String relativePath, long localSize)
        throws IOException {
    ftp.setFileType(FTP.BINARY_FILE_TYPE);
    changeWorkingDirectory(relativePath);

    this.ftp.enterLocalPassiveMode();
    FTPFile[] listFiles = this.ftp.listFiles(name);
    // long localSize = inputStream.available();// ? 不知道好用否
    if (listFiles.length == 1) {
        long remoteSize = listFiles[0].getSize();

        if (remoteSize == localSize) {
            this.setUploadStatus(UploadStatus.File_Exits);
            return this;
        } else if (remoteSize > localSize) {
            this.setUploadStatus(UploadStatus.Remote_Bigger_Local);
            return this;
        }
        this.uploadFile(inputStream, name, remoteSize, localSize);
    } else {
        this.uploadFile(inputStream, name, 0, localSize);
    }
    logger.info("{}/{} upload success", relativePath, name);
    return this;
}

private void uploadFile(InputStream inputStream, String name, long remoteSize, long localSize) throws IOException {
    this.ftp.enterLocalPassiveMode();
    OutputStream output = null;
    long step = localSize / 100;
    long process = 0;
    long localreadbytes = 0L;
    try {
        if (remoteSize > 0) {
            output = this.ftp.appendFileStream(name);
            this.ftp.setRestartOffset(remoteSize);
            inputStream.skip(remoteSize);
            process = remoteSize / step;
            localreadbytes = remoteSize;
        } else {
            output = this.ftp.storeFileStream(name);
        }
        byte[] bytes = new byte[1024];
        int c;
        while ((c = inputStream.read(bytes)) != -1) {
            output.write(bytes, 0, c);
            localreadbytes += c;
            if (localreadbytes / step >= process + 10) {
                process = localreadbytes / step;
                logger.info("文件【" + name + "】上传ftp进度汇报, process = " + process);
            }
        }
        logger.info("文件" + name + "上传ftp进度汇报, process = " + 100);
        output.flush();
        inputStream.close();
        output.close();
        boolean result = this.ftp.completePendingCommand();
        if (remoteSize > 0) {
            this.setUploadStatus(
                    result ? UploadStatus.Upload_From_Break_Success : UploadStatus.Upload_From_Break_Failed);
        } else {
            this.setUploadStatus(
                    result ? UploadStatus.Upload_New_File_Success : UploadStatus.Upload_New_File_Failed);
        }
    } catch (Exception e) {
        this.setUploadStatus(
                remoteSize > 0 ? UploadStatus.Upload_From_Break_Failed : UploadStatus.Upload_New_File_Failed);
    }

}

public OutputStream upload(String name, String relativePath) throws IOException {
    ftp.setFileType(FTP.BINARY_FILE_TYPE);
    changeWorkingDirectory(relativePath);
    ftp.enterLocalPassiveMode();
    return this.ftp.storeFileStream(name);
}

public void changeWorkingDirectory(String relativePath) throws IOException {
    if (relativePath == null) {
        throw new NullPointerException("relativePath can't be null");
    }
    String[] dirs = relativePath.split("/");
    for (String dir : dirs) {
        if (!this.ftp.changeWorkingDirectory(dir)) {
            if (this.ftp.makeDirectory(dir)) {
                this.ftp.changeWorkingDirectory(dir);
            } else {
                logger.warn("{}目录创建失败, 导致不能进入合适的目录进行上传", dir);
            }
        }
    }
}

/**
 * ftp上传目录下所有文件的功能
 * 
 * @param file
 *            要上传的目录
 * @param relativePath
 *            要上传到ftp服务器的相对路径
 * @return
 * @throws IOException
 */
public FtpClientUtil uploadDir(File file, String relativePath) throws IOException {
    if (!file.isDirectory()) {
        throw new IllegalArgumentException("file argument is not a directory!");
    }
    relativePath = relativePath + "/" + file.getName();
    File[] listFiles = file.listFiles();
    for (File f : listFiles) {
        this.uploadFree(f, relativePath);
    }
    return this;
}

/**
 * ftp上传文件, 调用方不用区分文件是否为目录,由该方法自己区分处理
 * 
 * @param file
 *            要上传的文件
 * @param relativePath
 *            要上传到ftp服务器的相对路径
 * @return
 * @throws IOException
 */
public FtpClientUtil uploadFree(File file, String relativePath) throws IOException {
    if (file.isDirectory()) {
        this.uploadDir(file, relativePath);
    } else {
        this.upload(file, relativePath);
    }
    return this;
}

/**
 * 本方法是上传的快捷方法,方法中自身包含了ftp 连接、登陆、上传、退出、断开各个步骤
 * 
 * @param file
 *            要上传的文件
 * @param relativePath
 *            要上传到ftp服务器的相对路径
 */
public boolean uploadOneStep(File file, String relativePath) {
    try {
        this.connect().login().uploadFree(file, relativePath);
        return true;
    } catch (IOException e) {
        String msg = String.format("ftp上传时发生异常, filename = [%s], relativePath = [%s]", file.getName(),
                relativePath);
        logger.error(msg, e);
        return false;
    } finally {
        this.disconnectFinally();
    }
}

public boolean uploadOneStepForStream(InputStream inputStram, String name, String relativePath, long localSize) {
    try {
        this.connect().login().upload(inputStram, name, relativePath, localSize);
        return true;
    } catch (IOException e) {
        String msg = String.format("ftp上传时发生异常, filename = [%s], relativePath = [%s]", name, relativePath);
        logger.error(msg, e);
        return false;
    } finally {
        this.disconnectFinally();
    }
}

public interface OutputStreamForUpload {
    public void write(OutputStream outputStream) throws IOException;
}

public boolean uploadOneStepForStream(OutputStreamForUpload outputUpload, String name, String relativePath) {
    try {
        this.connect().login();
        OutputStream upload = this.upload(name, relativePath);
        outputUpload.write(upload);
        return true;
    } catch (IOException e) {
        String msg = String.format("ftp上传时发生异常, filename = [%s], relativePath = [%s]", name, relativePath);
        logger.error(msg, e);
        return false;
    } finally {
        this.disconnectFinally();
    }
}

public FtpClientUtil logout() throws IOException {
    this.ftp.logout();
    return this;
}

public void disconnect() {
    this.disconnectFinally();
}

private void disconnectFinally() {
    if (this.ftp.isConnected()) {
        try {
            this.ftp.disconnect();
        } catch (IOException ioe) {
            logger.warn("ftp断开服务器链接异常", ioe);
        }
    }
}

@Override
public String toString() {
    return "FtpClientHelper [host=" + host + ", port=" + port + ", username=" + username + ", password=" + password
            + "]";
}

}
看代码吧:

相关文章
|
1月前
|
存储 Java 关系型数据库
高效连接之道:Java连接池原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。频繁创建和关闭连接会消耗大量资源,导致性能瓶颈。为此,Java连接池技术通过复用连接,实现高效、稳定的数据库连接管理。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接池的基本操作、配置和使用方法,以及在电商应用中的具体应用示例。
74 5
|
1月前
|
SQL Java 数据库连接
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率。本文介绍了连接池的工作原理、优势及实现方法,并提供了HikariCP的示例代码。
53 3
|
1月前
|
Java 数据库连接 数据库
深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能
在Java应用开发中,数据库操作常成为性能瓶颈。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能。文章介绍了连接池的优势、选择和使用方法,以及优化配置的技巧。
39 1
|
1月前
|
Java 数据库连接 数据库
Java连接池在数据库性能优化中的重要作用。连接池通过预先创建和管理数据库连接,避免了频繁创建和关闭连接的开销
本文深入探讨了Java连接池在数据库性能优化中的重要作用。连接池通过预先创建和管理数据库连接,避免了频繁创建和关闭连接的开销,显著提升了系统的响应速度和吞吐量。文章介绍了连接池的工作原理,并以HikariCP为例,展示了如何在Java应用中使用连接池。通过合理配置和优化,连接池技术能够有效提升应用性能。
53 1
|
2月前
|
SQL Java 关系型数据库
java连接mysql查询数据(基础版,无框架)
【10月更文挑战第12天】该示例展示了如何使用Java通过JDBC连接MySQL数据库并查询数据。首先在项目中引入`mysql-connector-java`依赖,然后通过`JdbcUtil`类中的`main`方法实现数据库连接、执行SQL查询及结果处理,最后关闭相关资源。
154 6
|
1月前
|
中间件 Docker Python
【Azure Function】FTP上传了Python Function文件后,无法在门户页面加载函数的问题
通过FTP上传Python Function至Azure云后,出现函数列表无法加载的问题。经排查,发现是由于`requirements.txt`中的依赖包未被正确安装。解决方法为:在本地安装依赖包到`.python_packages/lib/site-packages`目录,再将该目录内容上传至云上的`wwwroot`目录,并重启应用。最终成功加载函数列表。
|
2月前
|
SQL 存储 Java
Java中使用ClickHouseDriver连接和基本操作
通过上述步骤,你可以轻松地在Java应用中集成ClickHouse数据库,执行基本的CRUD操作。需要注意的是,实际开发中应当根据实际情况调整数据库连接配置(如URL中的主机、端口、数据库名等),并根据应用需求选择合适的异常处理策略,确保代码的健壮性和资源的有效管理。此外,对于复杂查询和大批量数据处理,建议充分利用ClickHouse的特性(如分布式处理、列式存储优化等),以进一步提升性能。
188 2
|
2月前
|
网络安全 Windows
Jetson 学习笔记(十五):FTP协议传输文件
本文介绍了如何使用WinSCP软件通过FTP协议在Windows和Jetson设备之间传输文件,并分享了一些操作经验和技巧。
34 0
Jetson 学习笔记(十五):FTP协议传输文件
|
8天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
37 6
|
22天前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####