java通过FTP跨服务器动态监听读取指定目录下文件数据

news2024/11/23 22:42:54

背景:

1、文件数据在A服务器(windows)(不定期在指定目录下生成),项目应用部署在B服务器(Linux);
2、项目应用在B服务器,监听A服务器指定目录,有新生成文件,进行读取文件信息,持久化数据;
3、提供两块内容,第一安装windows FTP服务;第二项目源码,希望可以帮助到你。

共计4种方案,试错采用了第三种方案,第四种方案没有试。

1、使用jcsh.jar提供方法读取文件信息,但需要A服务器开通SSH远程连接,一般linux服务器都是默认开通的,可直接读取连接读取,windows系统需安装SSH,因现场环境A服务器是windows2003,故放弃这种方法。
2、曲线救国,通过脚本(脚本监听比较困难,故放弃)把A服务器信息定时存入B服务器(Linux),再通过jcsh.jar读取文件信息。
3、通过A服务器安装FTP服务,B服务器安装FTP客户端,使用java动态监听该目录下生成文件读取信息。
4、把A服务器指定目录进行共享(等同于共享的这个目录就是B服务的目录了),再进行读取,因第三种方案成功,故没有尝试第四种方案。

windows安装FTP服务

1、开启ftp服务:控制面板–程序和功能–启用或关闭windows功能–标红框全部打开–点击确定
在这里插入图片描述
2、新建站点:
控制面板–大图标–管理工具
在这里插入图片描述
IIS管理器
在这里插入图片描述
网站–添加FTP站点
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以上就是windows安装FTP服务的过程,我这演示了匿名创建站点,谁都可以访问,还可以新建用户,需要用户登录才能访问。

源码

引入该依赖

<dependency>
   <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.6</version>
</dependency>

FileChangeData

@Data
public class FileChangeData {

    /**
     * 文件信息
     * */
    private FTPFile ftpFile;

    /**
     * 文件改变类型
     * */
    private FileChangeType eventType;

    /**
     * 文件名称
     * */
    private   String fileName;

    /**
     * 文件大小
     * */
    private Long fileSize;

    /**
     * FTPClient
     * */
    private FTPClient ftpClient;

    /**
     * 获取文件输入流
     * @return InputStream
     * */
    public InputStream getInputStream(String filePathName) {
        //如果是删除事件则不能够获取流
        if (Objects.equals(eventType, FileChangeType.FILE_DELETED)) {
            return null;
        }

        try {
            return ftpClient.retrieveFileStream(filePathName);
        } catch (IOException e) {
            return null;
        }
    }
}

FileChangeEvent

public interface FileChangeEvent {

    /**
     * 文件发生改变时触发此方法
     * @param fileChangeData 文件发生了改变
     * */
    @Function
    void change(FileChangeData fileChangeData) throws IOException;
}

FTPService

public interface FTPService {

    /**
     * ftp登陆
     * @return boolean 是否登陆成功
     * */
    FTPClient login();

    /**
     * ftp登出
     * @return boolean 是否登出成功
     * */
    boolean loginOut();

    /**
     * 获取文件列表
     * @return FTPFile[] 文件列表
     * */
    FTPFile[] listFile();

    /**
     * 监听文件夹的改变
     * @param fileChangeEvent 文件改变事件
     * */
    void addListenerFileChange(FileChangeEvent fileChangeEvent);
}

ListenerChangeRunnable

public interface ListenerChangeRunnable extends Runnable {

    /**
     * 停止监听文件
     * @return boolean 是否停止成功
     * */
    boolean stopListener();
}

FTPServiceImpl

@Service
public class FTPServiceImpl implements FTPService {

    @Autowired
    private FTPConfig ftpConfig;

    private String SPLIT = ":";

    private ThreadLocal<FTPClient> currentFTPClient;

    private ThreadLocal<ListenerChangeRunnable> currentListener;

    public FTPServiceImpl() {
        this.currentFTPClient = new ThreadLocal<>();
        this.currentListener = new ThreadLocal<>();
    }

    @Override
    public FTPClient login() {
        FTPClient ftpClient = new FTPClient();
        try {
            ftpClient.connect(ftpConfig.getFtpIp(), ftpConfig.getFtpPort());
            ftpClient.login(ftpConfig.getUsername(), ftpConfig.getPassword());
//            ftpClient.setControlEncoding("gb2312");
            this.currentFTPClient.set(ftpClient);
            return ftpClient;
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    public boolean loginOut() {
        try {
            currentFTPClient.get().logout();
            currentFTPClient.get().disconnect();
            return Boolean.TRUE;
        } catch (Exception e) {
            return Boolean.FALSE;
        }
    }

    @Override
    public FTPFile[] listFile() {
        FTPClient ftpClient = this.currentFTPClient.get();
        try {
            return ftpClient.listFiles();
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    public void addListenerFileChange(FileChangeEvent fileChangeEvent) {
        FTPClient ftpClient = this.currentFTPClient.get();
        ListenerFileChangeThreadRunnable listenerFileChangeThread = new ListenerFileChangeThreadRunnable(ftpClient, fileChangeEvent);
        this.currentListener.set(listenerFileChangeThread);
        new Thread(listenerFileChangeThread).start();
    }
}

ListenerFileChangeThreadRunnable

@Slf4j
public class ListenerFileChangeThreadRunnable implements ListenerChangeRunnable {

    private final FTPClient ftpClient;

    private volatile boolean stop;

    private final Map<String, Long> fileMemory;

    private final FileChangeEvent fileChangeEvent;

    public ListenerFileChangeThreadRunnable(FTPClient ftpClient, FileChangeEvent fileChangeEvent) {
        this.ftpClient = ftpClient;
        this.fileChangeEvent = fileChangeEvent;
        this.fileMemory = new HashMap<>();
    }

    @Override
    public void run() {
        while (!stop) {
            try {
                FTPFile[] ftpFiles = ftpClient.listFiles();

                //判断文件被删除
                if (fileMemory.size() > 0) {
                    Set<String> fileNames = new HashSet<>();
                    for (FTPFile ftpFile : ftpFiles) {
                        if (ftpFile.isDirectory()) {
                            log.info("文件夹不做删除判断");
                            continue;
                        }
                        fileNames.add(ftpFile.getName());
                    }
                    Set<Map.Entry<String, Long>> entries = fileMemory.entrySet();
                    for (Map.Entry<String, Long> map : entries) {
                        if (!fileNames.contains(map.getKey())) {
                            log.info("文件{}被删除了", map.getKey());
                            FileChangeData fileChangeData = new FileChangeData();
                            fileChangeData.setEventType(FileChangeType.FILE_DELETED);
                            fileChangeData.setFileName(map.getKey());
                            fileChangeData.setFileSize(map.getValue());
                            fileMemory.remove(map.getKey());
                            entries.remove(map.getKey());
                            fileChangeEvent.change(fileChangeData);
                        }
                    }
                }
                //判断文件是否有更改或新增
                for (FTPFile ftpFile: ftpFiles) {
                    //判断是否为文件夹
                    if (ftpFile.isDirectory()) {
//                        log.info("{}为文件不进行监听操作", ftpFile.getName());
                        continue;
                    }
                    FileChangeData fileChangeData = new FileChangeData();
                    fileChangeData.setFileName(ftpFile.getName());
//                    fileChangeData.setFileName("D:\\ftptest\\aaa\\"+ftpFile.getName());
                    fileChangeData.setFileSize(ftpFile.getSize());
                    fileChangeData.setFtpFile(ftpFile);
                    //文件是否存在于缓存文件列表中
                    if (fileMemory.containsKey(ftpFile.getName())) {
//                        log.info("文件{}在内存中已经存在,进行大小判断", ftpFile.getName());
                        if (!Objects.equals(fileMemory.get(ftpFile.getName()), ftpFile.getSize())) {
//                            log.info("文件{}在内存中已经存在且大小不一致,进行更新缓存操作", ftpFile.getName());
                            fileMemory.put(ftpFile.getName(), ftpFile.getSize());
                            fileChangeData.setEventType(FileChangeType.FILE_UPDATE);
                            fileChangeEvent.change(fileChangeData);
                        }
                        continue;
                    }
//                    log.info("文件{}在内存中不存在进行缓存操作", ftpFile.getName());
                    fileMemory.put(ftpFile.getName(), ftpFile.getSize());
                    fileChangeData.setEventType(FileChangeType.FILE_ADD);
                    fileChangeEvent.change(fileChangeData);
                }
            } catch (Exception e) {
                continue;
                //throw new RuntimeException(e);
            }
            try {
                TimeUnit.SECONDS.sleep(20);
            } catch (InterruptedException e) {
                continue;
                //throw new RuntimeException(e);
            }
        }
    }

    @Override
    public boolean stopListener() {
        this.stop = Boolean.TRUE;
        this.fileMemory.clear();
        return this.stop;
    }
}

FileChangeType

public enum FileChangeType {
    FILE_UPDATE(0, "文件更新"),
    FILE_ADD(1, "文件添加"),
    FILE_DELETED(2, "文件删除");

    @Getter
    private Integer type;

    @Getter
    private String desc;

    FileChangeType(Integer type, String desc) {
        this.type = type;
        this.desc = desc;
    }
}

FTPConfig

@Data
@Configuration
public class FTPConfig {

    @Value("${ftp.ip:ftp的ip}")
    private String ftpIp;

    @Value("${ftp.port:ftp端口,默认21}")
    private Integer ftpPort;

    @Value("${ftp.username:ftp创建的用户名}")
    private String username;

    @Value("${ftp.password:ftp创建的用户名密码}")
    private String password;
}

SendEmailApplicationTests

@Component
class SendEmailApplicationTests implements ApplicationRunner {
    @Autowired
    private FTPService ftpService;
    void ftpTest() {
        FTPClient ftpClient = ftpService.login();
        ftpService.addListenerFileChange(ftpFile -> {
            System.out.println(String.format("文件%s被改变了,文件改变类型%s", ftpFile.getFileName(), ftpFile.getEventType().getDesc()));
            InputStream inputStream = ftpClient.retrieveFileStream("/"+ ftpFile.getFileName());
            if(inputStream!=null){
                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream,"GBK"));
                String s = null;
                List<String> listStr = new ArrayList<>();//读取的数据在listStr
                while ((s = reader.readLine()) != null) {
                    System.out.println("===================>" + s);
                    listStr.add(s);
                }
                //处理业务逻辑
                
                inputStream.close();
                reader.close();
                ftpClient.completePendingCommand();
            }
        });
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        ftpTest();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1189612.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UI设计软件有哪些好用和免费的吗?

在我们分享五个有用的原型工具之前&#xff0c;完成原型&#xff0c;将优化界面&#xff0c;这次是UI设计师的任务&#xff0c;UI设计软件对设计师非常重要&#xff0c;UI设计工具是否使用直接影响最终结果&#xff0c;然后有人会问&#xff1a;UI界面设计使用什么软件&#xf…

2021年12月 Python(四级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试&#xff08;1~6级&#xff09;全部真题・点这里 一、单选题&#xff08;共25题&#xff0c;每题2分&#xff0c;共50分&#xff09; 第1题 Python 中函数不包括&#xff1f; A&#xff1a;标准函数 B&#xff1a;第三库函数 C&#xff1a;内建函数 D&#xf…

如何判断被DDoS攻击

当网络和设备正常的情况下&#xff0c;服务器突然出现连接断开、访问卡顿、用户掉线等情况;服务器CPU或内存占用率出现明显增长;网络出入流量出现明显增长;网站或应用程序突然出现大量的未知访问;登录服务器失败或者登录过慢等等。以上是最为常见的服务器被 DDoS攻击后出现的几…

【赠书第2期】嵌入式虚拟化技术与应用

文章目录 前言 1 背景概述 2 专家推荐 3 本书适合谁&#xff1f; 4 内容简介 5 书籍目录 6 权威作者团队 7 粉丝福利 前言 随着物联网设备的爆炸式增长和万物互联应用的快速发展&#xff0c;虚拟化技术在嵌入式系统上受到了业界越来越多的关注、重视和实际应用。嵌入式…

鸿蒙开发工具的汉化

1、下载汉化包 汉化插件下载地址&#xff1a;Chinese (Simplified) Language Pack / 中文语言包 - IntelliJ IDEs Plugin | Marketplace 百度网盘下载地址&#xff1a;链接&#xff1a;百度网盘 请输入提取码 DevEco Studio是基于IDEA223版本&#xff0c;下载汉化包时请注意…

【经验模态分解】2.EMD的3个基本概念

/*** poject 经验模态分解及其衍生算法的研究及其在语音信号处理中的应用* file EMD的3个基本概念* author jUicE_g2R(qq:3406291309)* * language MATLAB/Python/C/C* EDA Base on matlabR2022b* editor Obsidian&#xff08;黑曜石笔记软件…

JVM之jinfo虚拟机配置信息工具

jinfo虚拟机配置信息工具 1、jinfo jinfo&#xff08;Configuration Info for Java&#xff09;的作用是实时地查看和调整虚拟机的各项参数。 使用jps -v 可以查看虚拟机启动时显示指定的参数列表&#xff0c;但是如果想知道未被显示指定的参数的系统默认值&#xff0c;除 …

SpringBoot测试类启动web环境-下篇

一、响应状态 1.MockMvcResultMatchers 说明&#xff1a;模拟结果匹配。 package com.forever;import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoC…

数据分析实战 | 多元回归——广告收入数据分析

目录 一、数据及分析对象 二、目的及分析任务 三、方法及工具 四、数据读入 五、数据理解 六、数据准备 七、模型构建 八、模型预测 九、模型评价 一、数据及分析对象 CSV格式的数据文件——“Advertising.csv” 数据集链接&#xff1a;https://download.csdn.net/d…

Jmeter 性能压测 —— 混合场景

性能测试&#xff0c;单场景的目的一般是为了发现缺陷、发现瓶颈。 完成所有单个重点场景的性能测试之后&#xff0c;还需要做一个混合场景的性能测试-评估系统整体性能。 1、场景设计 使用Jmeter 做混合场景设计 在一个测试计划&#xff0c;将每个重点测试场景各创建为一个…

第五章:人工智能深度学习教程-人工神经网络(第一节-人工神经网络及其应用)

当您阅读这篇文章时&#xff0c;您体内的哪个器官正在思考这个问题&#xff1f;当然是大脑啦&#xff01;但你知道大脑是如何运作的吗&#xff1f;嗯&#xff0c;它有神经元或神经细胞&#xff0c;它们是大脑和神经系统的主要单位。这些神经元接收来自外界的感觉输入并进行处理…

基于SSM的电动车上牌管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

Qt 自定义控件-支持换行和点击事件的Label

目录 前言1、功能描述2、代码实现2.1 头文件2.2 源码文件2.3 设计思路 3、示例4、总结 前言 本文主要介绍一个自定义控件&#xff0c;支持换行和点击事件的Label。起因是有这样一个需求&#xff0c;在一个复杂系统中有一个页面可以显示该系统中所有设备的名字&#xff0c;并且…

RSA 2048位算法的主要参数N,E,P,Q,DP,DQ,Qinv,D分别是什么意思 哪个是通常所说的公钥与私钥 -安全行业基础篇5

非对称加密算法RSA 在RSA 2048位算法中&#xff0c;常见的参数N、E、P、Q、DP、DQ、Qinv和D代表以下含义&#xff1a; N&#xff08;Modulus&#xff09;&#xff1a;模数&#xff0c;是两个大素数P和Q的乘积。N的长度决定了RSA算法的安全性。 E&#xff08;Public Exponent&a…

基于SSM的小区物业管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

获取请求IP以及IP解析成省份

某些业务需要获取请求IP以及将IP解析成省份之类的&#xff0c;于是我写了一个工具类&#xff0c;可以直接COPY /*** IP工具类* author xxl* since 2023/11/9*/ Slf4j public class IPUtils {/*** 过滤本地地址*/public static final String LOCAL_ADDRESS "127.0.0.1&quo…

基于SSM的演唱会购票系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue、HTML 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是…

Langchain-Chatchat实践详解

简介 本质上是在Langchain基础上封装的一层聊天服务&#xff0c;可以对接底层多种离线LLM和在线的LLM&#xff08;也可以对接自定义的在线LLM&#xff09;。提供基于知识库聊天功能相关的一系列API。 下载源码 源码地址&#xff1a; https://github.com/chatchat-space/Lang…

Apache Druid连接回收引发的血案

问题 线上执行大批量定时任务&#xff0c;发现SQL执行失败的报错&#xff1a; CommunicationsException, druid version 1.1.10, jdbcUrl : jdbc:mysql://xxx?useUnicodetrue&characterEncodingUTF-8&zeroDateTimeBehaviorconvertToNull,testWhileIdle true, idle …