SpringBoot使用hutool操作FTP的详细过程

 更新时间:2024年09月25日 09:33:21   作者:涛哥是个大帅比  
在使用SpringBoot结合hutool操作FTP时,遇到防火墙导致上传文件大小为0kb的问题,通过设置FTP为被动模式解决,本文详细解析了FTP的主动模式和被动模式的工作原理、安全性及适用场景,帮助理解FTP的连接方式和解决网络限制问题

项目场景:

<dependency>
	<groupId>commons-net</groupId>
	<artifactId>commons-net</artifactId>
	<version>3.9.0</version>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.15</version>
</dependency>

实现步骤:

1、引入依赖

<dependency>
	<groupId>commons-net</groupId>
	<artifactId>commons-net</artifactId>
	<version>3.9.0</version>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.15</version>
</dependency>

2、yml配置 

ftp:
  # 服务器地址
  host: 127.0.0.1
  # 端口号
  port: 21
  # 用户名
  userName: test
  # 密码
  password: test

3、Config配置类

 我这里用的是static修饰的变量,方便工具类调用。

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
 * ftp配置
 */
@Configuration
public class FtpConfig {
    /**
     * 服务器地址
     */
    private static String host;
    /**
     * 端口
     */
    private static Integer port;
    /**
     * 用户名
     */
    private static String userName;
    /**
     * 密码
     */
    private static String password;
    @Value("${ftp.host}")
    public void setHost(String host) {
        FtpConfig.host = host;
    }
    public static String getHost() {
        return host;
    }
    @Value("${ftp.port}")
    public void setPort(Integer port) {
        FtpConfig.port = port;
    }
    public static Integer getPort() {
        return port;
    }
    @Value("${ftp.userName}")
    public void setUserName(String userName) {
        FtpConfig.userName = userName;
    }
    public static String getUserName() {
        return userName;
    }
    @Value("${ftp.password}")
    public void setPassword(String password) {
        FtpConfig.password = password;
    }
    public static String getPassword() {
        return password;
    }
}

4、 FtpUtil工具类

import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.ftp.Ftp;
import cn.hutool.extra.ftp.FtpMode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTPFile;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
 * FTP服务工具类
 */
@Slf4j
public class FtpUtil {
    /**
     * 获取 FTPClient对象
     */
    private static Ftp getFTPClient() {
        try {
            if(StringUtils.isBlank(FtpConfig.getHost()) || FtpConfig.getPort() == null
                || StringUtils.isBlank(FtpConfig.getUserName()) || StringUtils.isBlank(FtpConfig.getPassword())) {
                throw new RuntimeException("ftp配置信息不能为空");
            }
            Ftp ftp = new Ftp(FtpConfig.getHost(),FtpConfig.getPort(),FtpConfig.getUserName(),FtpConfig.getPassword());
            //设置为被动模式,防止防火墙拦截
            ftp.setMode(FtpMode.Passive);
            return ftp;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("获取ftp客户端异常",e);
            throw new RuntimeException("获取ftp客户端异常:"+e.getMessage());
        }
    }
    /**
     * 下载ftp服务器上的文件到本地
     * @param remoteFile    ftp上的文件路径
     * @param localFile     输出的目录,使用服务端的文件名
     */
    public static void download(String remoteFile, String localPath) {
        if(StringUtils.isBlank(remoteFile) || StringUtils.isBlank(localPath)) {
            return;
        }
        Ftp ftp = getFTPClient();
        try {
            if(!FileUtil.exist(localPath)){
                FileUtil.mkdir(localPath);
            }    
            File lFile = FileUtil.file(localPath);
            ftp.download(remoteFile, lFile);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("FTP文件下载异常",e);
        } finally {
            //关闭连接
            try {
                if(ftp != null)  ftp.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
    /**
     * 本地文件上传到ftp服务器上
     * @param remoteDir 上传的ftp目录
     * @param remoteFileName  保存到ftp服务器上的名称
     * @param localFile 本地文件全名称
     */
    public static boolean upload(String remoteDir, String remoteFileName, String localFile) {
        if(StringUtils.isBlank(remoteDir) || StringUtils.isBlank(remoteFileName) || StringUtils.isBlank(localFile)) {
            return false;
        }
        Ftp ftp = getFTPClient();
        try {
            File lFile = FileUtil.file(localFile);
            if(!lFile.exists()) {
                log.error("本地文件不存在");
                return false;
            }
            if(StringUtils.isBlank(remoteFileName)) {
                return ftp.upload(remoteDir, lFile);
            } else {
                return ftp.upload(remoteDir, remoteFileName, lFile);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("文件上传FTP异常",e);
            return false;
        } finally {
            //关闭连接
            try {
                if(ftp != null)  ftp.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
    /**
     * 删除FTP服务器中的文件
     * @param remoteFile    ftp上的文件路径
     */
    public static boolean delFile(String remoteFile) {
        if(StringUtils.isBlank(remoteFile)) {
            return false;
        }
        Ftp ftp = getFTPClient();
        try {
            return ftp.delFile(remoteFile);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("删除FTP服务器中的文件异常",e);
            return false;
        } finally {
            //关闭连接
            try {
                if(ftp != null)  ftp.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
    /**
     * 遍历某个目录下所有文件,不会递归遍历
     * @param path    目录
     */
    public static List<String> listFile(String path) {
        List<String> listFile = new ArrayList<>();
        Ftp ftp = getFTPClient();
        try {
            FTPFile[] ftpFiles = ftp.lsFiles(path);
            for (int i = 0; i < ftpFiles.length; i++) {
                FTPFile ftpFile = ftpFiles[i];
                if(ftpFile.isFile()){
                    listFile.add(ftpFile.getName());
                }
            }
            return listFile;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("遍历某个目录下所有文件异常",e);
            return null;
        } finally {
            //关闭连接
            try {
                if(ftp != null)  ftp.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

5、测试 

@RestController
@RequestMapping("/test")
public class TestController {
	@RequestMapping(value = "ftpTest", method = RequestMethod.GET)
	public void ftpTest() {
		//上传文件到ftp
		FtpUtil.upload("opt/upload","APP_RELATION.sql", "F:/APP_RELATION.sql");
		//下载远程文件
		FtpUtil.download("opt/upload/APP_RELATION.sql", "D:/");
		//删除远程文件
		FtpUtil.delFile("opt/upload/APP_RELATION.sql");
	}
}

总结:

上传的时候碰到一个问题,就是本地开启防火墙时,上传的文件大小是0kb,必须要关了防火墙才正常,后来FTP模式设置为“被动模式”解决。

//设置为被动模式,防止防火墙拦截
ftp.setMode(FtpMode.Passive);

主动模式(Active Mode):

  • 工作原理:客户端在本地打开一个非特权端口(通常大于1023),并通过这个端口发送PORT命令给服务器,告诉服务器客户端用于数据传输的端口号。然后,服务器使用其20端口(数据端口)主动连接到客户端指定的端口进行数据传输。
  • 安全性:由于服务器需要主动连接到客户端的端口,这可能引发一些安全问题,特别是当客户端位于防火墙或NAT设备后面时。
  • 适用场景:适用于客户端位于可以接受入站连接的网络环境,且没有防火墙或NAT设备限制的场景。

被动模式(Passive Mode): 

  • 工作原理:客户端发送PASV命令给服务器,服务器在本地打开一个端口(通常是高位的非特权端口),并通过PASV命令的响应告诉客户端这个端口号。然后,客户端主动连接到服务器指定的这个端口进行数据传输。
  • 安全性:由于客户端主动连接到服务器,这种模式更适合于客户端位于防火墙或NAT设备后面的场景,因为这些设备通常允许出站连接但限制入站连接。
  • 适用场景: 特别适用于网络环境不稳定、存在防火墙或NAT设备的场景。

到此这篇关于SpringBoot使用hutool操作FTP的文章就介绍到这了,更多相关SpringBoot使用hutool内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 解决Java包装类比较时遇到的问题

    解决Java包装类比较时遇到的问题

    所谓包装类的作用就是将原始数据类型转换成引用数据类型,下面这篇文章主要给大家介绍了关于在Java包装类比较时遇到的问题的解决方法,文中给出了详细的示例代码,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-09-09
  • Java实现滑动验证码的示例代码

    Java实现滑动验证码的示例代码

    这篇文章主要介绍了Java实现滑动验证码的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • java 中平方根(sqrt)算法 的实例详解

    java 中平方根(sqrt)算法 的实例详解

    这篇文章主要介绍了java 中平方根(sqrt)算法 的实例详解的相关资料,需要的朋友可以参考下
    2017-04-04
  • mybatis学习之路mysql批量新增数据的方法

    mybatis学习之路mysql批量新增数据的方法

    这篇文章主要介绍了mybatis学习之路mysql批量新增数据的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-02-02
  • 带你了解10道java入门面试题

    带你了解10道java入门面试题

    面试题相信大家都不陌生,想要一个好的工作面试题必不可少的,下面和小编一起来学习与了解Java当中有有些什么面试题吧,希望能给你带来帮助
    2021-08-08
  • Java 利用binarySearch实现抽奖计算逻辑

    Java 利用binarySearch实现抽奖计算逻辑

    这篇文章主要介绍了Java 利用binarySearch实现抽奖计算逻辑,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2020-12-12
  • 2018版java多线程面试题集合及答案

    2018版java多线程面试题集合及答案

    这篇文章主要为大家详细介绍了2018版java多线程面试题集合及答案,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-09-09
  • Java中HashMap的put过程详解

    Java中HashMap的put过程详解

    这篇文章主要介绍了Java中HashMap的put过程详解,HashMap有4个构造器,其他构造器如果用户没有传入initialCapacity 和loadFactor这两个参数,会使用默认值一般如果new HashMap()不传值,需要的朋友可以参考下
    2023-07-07
  • struts2实现简单文件下载功能

    struts2实现简单文件下载功能

    这篇文章主要为大家详细介绍了struts2实现简单文件下载功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • JMeter连接Mysql数据库的实现步骤

    JMeter连接Mysql数据库的实现步骤

    本文主要介绍了JMeter操作Mysql数据库,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12

最新评论