Java从ftp服务器上传与下载文件的实现

 更新时间:2023年08月08日 10:42:05   作者:明天再去学习  
这篇文章主要给大家介绍了关于Java从ftp服务器上传与下载文件的实现方法,最近项目中需要实现将文件先存放到ftp上,需要的时候再从ftp上下载,做的过程中碰到了问题,所以这里总结下,需要的朋友可以参考下

一、背景

业务需要从ftp服务器上上传、下载、删除文件等功能,通过查阅资料及手动敲打代码,实现了操作ftp的基本功能,有需求的小伙伴可以看看具体的实现过程。

二、ftp介绍

摘自百度百科:文件传输协议(File Transfer Protocol,FTP)是用于在网络上进行文件传输的一套标准协议,FTP允许用户以文件操作的方式(如文件的增、删、改、查、传送等)与另一主机相互通信。

三、具体代码实现

1、引入以下依赖

<!--apache下,包含连接ftp服务器的工具-->
<dependency>
    <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.6</version>
</dependency>

2、编写一个FTP工具类

含以下四个方法:

  • *获取一个FtpClinet连接
  • *关闭FtpClinet连接
  • *下载文件
  • *上传文件
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.FTPReply;
import java.io.*;
import java.nio.charset.StandardCharsets;
@Slf4j
public class FtpUtil {
    /**
     * 获取一个ftp连接
     * @param host ip地址
     * @param port 端口
     * @param username 用户名
     * @param password 密码
     * @return 返回ftp连接对象
     * @throws Exception 连接ftp时发生的各种异常
     */
    public static FTPClient getFtpClient(String host, Integer port, String username, String password) throws Exception{
        FTPClient ftpClient = new FTPClient();
        // 连接服务器
        ftpClient.connect(host, port);
        int reply = ftpClient.getReplyCode();
        if(!FTPReply.isPositiveCompletion(reply)){
            log.error("无法连接至ftp服务器, host:{}, port:{}", host, port);
            ftpClient.disconnect();
            return null;
        }
        // 登入服务器
        boolean login = ftpClient.login(username, password);
        if(!login){
            log.error("登录失败, 用户名或密码错误");
            ftpClient.logout();
            ftpClient.disconnect();
            return null;
        }
        // 连接并且成功登陆ftp服务器
        log.info("login success ftp server, host:{}, port:{}, user:{}", host, port, username);
        // 设置通道字符集, 要与服务端设置一致
        ftpClient.setControlEncoding("UTF-8");
        // 设置文件传输编码类型, 字节传输:BINARY_FILE_TYPE, 文本传输:ASCII_FILE_TYPE, 建议使用BINARY_FILE_TYPE进行文件传输
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
        // 动模式: enterLocalActiveMode(),被动模式: enterLocalPassiveMode(),一般选择被动模式
        ftpClient.enterLocalPassiveMode();
        // 切换目录
        //ftpClient.changeWorkingDirectory("xxxx");
        return ftpClient;
    }
    /**
     * 断开ftp连接
     * @param ftpClient ftp连接客户端
     */
    public static void disConnect(FTPClient ftpClient){
        if(ftpClient == null){
            return;
        }
        try {
            log.info("断开ftp连接, host:{}, port:{}", ftpClient.getPassiveHost(), ftpClient.getPassivePort());
            ftpClient.logout();
            ftpClient.disconnect();
        } catch (IOException e) {
            e.printStackTrace();
            log.error("ftp连接断开异常, 请检查");
        }
    }
    /**
     * 文件下载
     * @param ftpClient ftp连接客户端
     * @param path 文件路径
     * @param fileName 文件名称
     */
    public static void download(FTPClient ftpClient, String path, String fileName) throws Exception {
        if(ftpClient == null || path == null || fileName == null){
            return;
        }
        // 中文目录处理存在问题, 转化为ftp能够识别中文的字符集
        String remotePath;
        try {
            remotePath = new String(path.getBytes(StandardCharsets.UTF_8), FTP.DEFAULT_CONTROL_ENCODING);
        } catch (UnsupportedEncodingException e) {
            remotePath = path;
        }
        InputStream inputStream = ftpClient.retrieveFileStream(remotePath);
        if (inputStream == null) {
            log.error("{}在ftp服务器中不存在,请检查", path);
            return;
        }
        FileOutputStream outputStream = new FileOutputStream("D:\\test\\" + fileName);
        BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
        try{
            byte[] buffer = new byte[2048];
            int i;
            while ((i = bufferedInputStream.read(buffer)) != -1) {
                bufferedOutputStream.write(buffer, 0, i);
                bufferedOutputStream.flush();
            }
        } catch (Exception e) {
            log.error("文件下载异常", e);
            log.error("{}下载异常,请检查", path);
        }
        inputStream.close();
        outputStream.close();
        bufferedInputStream.close();
        bufferedOutputStream.close();
        // 关闭流之后必须执行,否则下一个文件导致流为空
        boolean complete = ftpClient.completePendingCommand();
        if(complete){
            log.info("文件{}下载完成", remotePath);
        }else{
            log.error("文件{}下载失败", remotePath);
        }
    }
    /**
     * 上传文件
     * @param ftpClient ftp连接客户端
     * @param sourcePath 源地址
     */
    public static void upload(FTPClient ftpClient, String sourcePath) throws Exception{
        if(ftpClient == null || sourcePath == null){
            return;
        }
        File file = new File(sourcePath);
        if(!file.exists() || !file.isFile()){
            return;
        }
        // 中文目录处理存在问题, 转化为ftp能够识别中文的字符集
        String remotePath;
        try {
            remotePath = new String(file.getName().getBytes(StandardCharsets.UTF_8), FTP.DEFAULT_CONTROL_ENCODING);
        } catch (UnsupportedEncodingException e) {
            remotePath = file.getName();
        }
        try(
                InputStream inputStream = new FileInputStream(file);
                OutputStream outputStream = ftpClient.storeFileStream(remotePath);
        ){
            byte[] buffer = new byte[2048];
            int length;
            while((length = inputStream.read(buffer)) != -1){
                outputStream.write(buffer, 0, length);
                outputStream.flush();
            }
        }catch (Exception e){
            log.error("文件上传异常", e);
        }
        // 关闭流之后必须执行,否则下一个文件导致流为空
        boolean complete = ftpClient.completePendingCommand();
        if(complete){
            log.info("文件{}上传完成", remotePath);
        }else{
            log.error("文件{}上传失败", remotePath);
        }
    }
}

3、测试连接、上传文件、下载文件、关闭连接功能

@Slf4j
@RestController
public class FtpController {
    private static final String FTP_HOST = "your host";
    private static final Integer FTP_PORT = 21;
    private static final String FTP_USERNAME = "your username";
    private static final String FTP_PASSWORD = "your password";
    @GetMapping("/test")
    public void test() throws Exception{
        FTPClient ftpClient = FtpUtil.getFtpClient(FTP_HOST, FTP_PORT, FTP_USERNAME, FTP_PASSWORD);
        // 展示文件夹
        FTPFile[] ftpFiles = ftpClient.listDirectories();
        for(FTPFile file : ftpFiles){
            System.out.println(file.getName());
        }
        // 下载文件
        FtpUtil.download(ftpClient, "test.txt", "test.txt");
        //FtpUtil.download(ftpClient, "test.py", "test.py");
        // 上传文件
        FtpUtil.upload(ftpClient, "D:\\test\\test.py");
        FtpUtil.disConnect(ftpClient);
    }
}

经过测试,能够正常连接上ftp服务器,并且上传下载文件,最后关闭连接。

四、遇到的问题

在使用过程中,遇到以下问题,需要记录一下,以防下次忘记:

就是在创建完一个FtpClient连接之后,关闭连接之前,第一次进行文件上传或下载是正常的,当第二次进行文件上传或下载时,从FtpClient获取的输入流或输出流是空的。需要在每次操作完文件上传或下载时,添加上以下代码片段:

// 关闭流之后必须执行,否则下一个文件导致流为空
boolean complete = ftpClient.completePendingCommand();
if(complete){
    log.info("文件{}下载完成", remotePath);
}else{
    log.error("文件{}下载失败", remotePath);
}

该方法有以下注释:

There are a few FTPClient methods that do not complete the entire sequence 
of FTP commands to complete a transaction. These commands require some action 
by the programmer after the reception of a positive intermediate command. 
After the programmer's code completes its actions, it must call this method 
to receive the completion reply from the server and verify the success of the 
entire transaction.

百度翻译大致意思应该是:有一些FTPClient方法不能完成完成事务的整个FTP命令序列。这些命令需要程序员在接收到肯定的中间命令后采取一些行动。程序员的代码完成其操作后,必须调用此方法以从服务器接收完成回复,并验证整个事务的成功。

这里不知道是不是描述的我这种情况, completePedingCommand的意思是完成挂起命令。可能我们当前场景就符合。刚兴趣的小伙伴可以深入去探讨一下。

五、参考连接

到此这篇关于Java从ftp服务器上传与下载文件的文章就介绍到这了,更多相关Java ftp服务器上传下载文件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java基础教程之Hello World到面向对象

    Java基础教程之Hello World到面向对象

    这篇文章主要介绍了Java基础教程之Hello World,Java入门教程,讲解了Java中的必备语法、编译与运行知识、数据类型等,需要的朋友可以参考下
    2014-08-08
  • Jmeter中的timeshift()函数获取当前时间进行加减

    Jmeter中的timeshift()函数获取当前时间进行加减

    这篇文章主要介绍了Jmeter中的timeshift()函数获取当前时间进行加减,TimeShift(格式,日期,移位,语言环境,变量)可对日期进行移位加减操作,本文给大家详细讲解,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-10-10
  • 基于Java实现中文分词系统的示例代码

    基于Java实现中文分词系统的示例代码

    这篇文章主要为大家详细介绍了如何利用Java语言实现一个简易的中文分词系统,文中的示例代码讲解详细,感兴趣的小伙伴可以尝试一下
    2022-07-07
  • java代码实现编译源文件

    java代码实现编译源文件

    这篇文章主要为大家详细介绍了Java通过 JavaCompiler 实现编译源文件的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2025-01-01
  • 实例详解Java调用第三方接口方法

    实例详解Java调用第三方接口方法

    很多项目都会封装规定好本身项目的接口规范,所以大多数需要去调用对方提供的接口或第三方接口(短信、天气等),下面这篇文章主要给大家介绍了关于Java调用第三方接口方法的相关资料,需要的朋友可以参考下
    2022-06-06
  • Java 如何将 byte[] 转换为 File 对象

    Java 如何将 byte[] 转换为 File 对象

    本文介绍了如何将字节数组(byte[])转换为文件对象(File)的方法,提供了具体代码示例,代码简单易懂,感兴趣的朋友跟随小编一起看看吧
    2025-03-03
  • 解决Servlet4.0版本使用注解设置url但无法访问的问题

    解决Servlet4.0版本使用注解设置url但无法访问的问题

    在学习servlet过程中,使用web.xml文件配置servlet可以正常访问,但使用WebServlet注解时出现404错误,解决方法是在web.xml文件中将metadata-complete属性改为false,启动标注支持,然而该方法对我无效,最后通过重建项目和手动将新建的项目添加到tomcat服务器解决问题
    2024-10-10
  • Springboot集成Camunda使用Mysql介绍

    Springboot集成Camunda使用Mysql介绍

    大家好,本篇文章主要讲的是Springboot集成Camunda使用Mysql介绍,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • Spring Cloud动态配置刷新@RefreshScope与@Component的深度解析

    Spring Cloud动态配置刷新@RefreshScope与@Component的深度解析

    在现代微服务架构中,动态配置管理是一个关键需求,Spring Cloud 提供了 @RefreshScope 注解,允许应用在运行时动态更新配置,而无需重启服务,本文深入探析Spring Cloud动态配置刷新@RefreshScope与@Component,感兴趣的朋友一起看看吧
    2025-04-04
  • Java中logback 自动刷新不生效的问题解决

    Java中logback 自动刷新不生效的问题解决

    本文主要介绍了Java中logback 自动刷新不生效的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05

最新评论