SpringBoot使用hutool操作FTP

news/2024/9/28 20:11:56 标签: ftp, ftp上传, ftp下载, 主动模式, 被动模式

项目场景:

SpringBoot使用hutool操作FTP,可以实现从FTP服务器下载文件到本地,以及将本地文件上传到FTP服务器的功能。


实现步骤:

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设备的场景。

http://www.niftyadmin.cn/n/5681767.html

相关文章

干货 | 2024大模型十大趋势(免费下载)

导读&#xff1a;近日&#xff0c;在2024世界人工智能大会上&#xff0c;腾讯正式发布了《2024大模型十大趋势——走进“机器外脑”时代》报告。目前&#xff0c;这一报告正在AI产业界各大社群快速传播。报告中&#xff0c;腾讯研究院试图通过10个关键性的趋势&#xff0c;去理…

ScrapeGraphAl AI爬虫

官网&#xff1a;https://scrapegraph-ai.readthedocs.io/en/latest/ from flask import Flask, request, jsonify from scrapegraphai.graphs import SmartScraperGraphapp Flask(__name__)openai_key "sk-xxxxxxxxxxxxxxxxxxxx"graph_config {"llm":…

babylon.js-1:入门篇

最近项目中使用到了 Babylon.js 这门技术&#xff0c;从今天开始&#xff0c;抽取自己写的比较好的拿出来&#xff0c;作为分享案例&#xff1a; 记录学习成果通过笔记的方式记录技术积累方便工作中查找翻阅实现案例 是什么 Babylon.js是一个基于WebGL的开源3D渲染引擎&…

基于冲突动态监测算法的健身房预约管理系统

系统展示 用户前台界面 管理员后台界面 系统背景 随着健身热潮的兴起&#xff0c;健身房管理面临着日益增长的会员需求与资源分配的挑战。传统的人工预约方式不仅效率低下&#xff0c;且容易出现时间冲突和资源浪费的情况。为了解决这一问题&#xff0c;基于冲突动态监测算法的…

Python神经求解器去耦合算法和瓦瑟斯坦距离量化评估

&#x1f3af;要点 神经求解器求解对偶方程&#xff0c;并学习两个空间之间的单调变换&#xff0c;最小化它们之间的瓦瑟斯坦距离。使用概率密度函数解析计算&#xff0c;神经求解器去耦合条件正则化流使用变量变换公式的生成模型瓦瑟斯坦距离量化评估神经求解器 &#x1f36…

最优化理论与自动驾驶(二-补充):求解算法(梯度下降法、牛顿法、高斯牛顿法以及LM法,C++代码)

在之前的章节里面&#xff08;最优化理论与自动驾驶&#xff08;二&#xff09;&#xff1a;求解算法&#xff09;我们展示了最优化理论的基础求解算法&#xff0c;包括高斯-牛顿法&#xff08;Gauss-Newton Method&#xff09;、梯度下降法&#xff08;Gradient Descent Metho…

在vscode在使用idea编辑器的快捷键

在vscode在使用idea编辑器的快捷键 在vscode扩展在搜索idea key结果如下&#xff1a; 选择IntelliJ IDEA Keybindings安装&#xff08;注意作者是Keisuke Kato&#xff09;&#xff0c;安装后就可以在vscode编辑器中使用idea编辑器的快捷键。

《掌握Shell脚本:从入门到精通的实用指南》

目录 引言 一、Shell变量——数组 二、表达式与运算符——表达式 &#xff08;一&#xff09;算术表达式 &#xff08;二&#xff09;逻辑表达式 &#xff08;三&#xff09;算术运算符 &#xff08;四&#xff09;整数关系运算符 &#xff08;五&#xff09;字符串检测…