当前位置: 首页 > news >正文

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

背景:

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 文件发生了改变* */@Functionvoid 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 {@Autowiredprivate FTPConfig ftpConfig;private String SPLIT = ":";private ThreadLocal<FTPClient> currentFTPClient;private ThreadLocal<ListenerChangeRunnable> currentListener;public FTPServiceImpl() {this.currentFTPClient = new ThreadLocal<>();this.currentListener = new ThreadLocal<>();}@Overridepublic 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;}}@Overridepublic boolean loginOut() {try {currentFTPClient.get().logout();currentFTPClient.get().disconnect();return Boolean.TRUE;} catch (Exception e) {return Boolean.FALSE;}}@Overridepublic FTPFile[] listFile() {FTPClient ftpClient = this.currentFTPClient.get();try {return ftpClient.listFiles();} catch (Exception e) {return null;}}@Overridepublic 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<>();}@Overridepublic 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);}}}@Overridepublic 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, "文件删除");@Getterprivate Integer type;@Getterprivate 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 {@Autowiredprivate 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<>();//读取的数据在listStrwhile ((s = reader.readLine()) != null) {System.out.println("===================>" + s);listStr.add(s);}//处理业务逻辑inputStream.close();reader.close();ftpClient.completePendingCommand();}});}@Overridepublic void run(ApplicationArguments args) throws Exception {ftpTest();}
}

相关文章:

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

背景&#xff1a; 1、文件数据在A服务器&#xff08;windows&#xff09;&#xff08;不定期在指定目录下生成&#xff09;&#xff0c;项目应用部署在B服务器&#xff08;Linux&#xff09;&#xff1b; 2、项目应用在B服务器&#xff0c;监听A服务器指定目录&#xff0c;有新…...

5G边缘计算网关的功能及作用

5G边缘计算网关具有多种功能。 首先&#xff0c;它支持智能云端控制&#xff0c;可以通过5G/4G/WIFI等无线网络将采集的数据直接上云&#xff0c;实现异地远程监测控制、预警通知、报告推送和设备连接等工作。 其次&#xff0c;5G边缘计算网关可以采集各种数据&#xff0c;包…...

阿里云AIGC小说生成【必得京东卡】

任务步骤 此文真实可靠不做虚假宣传&#xff0c;绝对真实&#xff0c;可截图为证。 领取任务 链接&#xff08;复制到wx打开&#xff09;&#xff1a;#小程序://ITKOL/1jw4TX4ZEhykWJd 教程实践 打开函数计算控制台 应用->创建应用->人工智能->通义千问 AI 助手-…...

数据结构之AVL树

map/multimap/set/multiset这几个容器有个共同点是: 其底层都是按照二叉搜索树来实现的,但是普通的二叉搜索树有其自身的缺陷, 假如往树中插入的元素有序或者接近有序, 二叉搜索树就会退化成单支树, 时间复杂度会退化成O(N),因此map、set等关联式容器的底层结构是对二叉树进行了…...

如何用Java实现一个基于机器学习的情感分析系统,用于分析文本中的情感倾向

背景&#xff1a;练习两年半&#xff08;其实是两周半&#xff09;&#xff0c;利用工作闲余时间入门一下机器学习&#xff0c;本文没有完整的可实施的案例&#xff0c;由于知识体系不全面&#xff0c;目前代码只能运行&#xff0c;不能准确的预测 卡点&#xff1a; 1 由于过…...

开发聚合支付的的意义

开发聚合支付的意义在于整合各种支付方式&#xff0c;为消费者和商家提供便捷高效的支付体验&#xff0c;同时满足商家的多元化支付需求&#xff0c;提高支付效率和用户体验。 具体来说&#xff0c;聚合支付具有以下意义&#xff1a; 方便快捷&#xff1a;聚合支付整合了多种…...

ChatGPT生产力|中科院学术ChatGPT优化配置

资源链接&#xff1a;GitHub - binary-husky/gpt_academic b站配置讲解链接&#xff1a;chatgpt-academic 新手运行官方精简指南&#xff08;科研chatgpt拓展&#xff09; 某知配置图文讲解&#xff1a;图文详解&#xff1a;在windows中部署ChatGPT学术版 - 知乎 (zhihu.com) 一…...

语音播报speechSynthesis最简单的例子(亲测有用)

最简单的例子&#xff0c;在chrome上亲测有效&#xff1a; const utterThis new SpeechSynthesisUtterance(我来试试呀); const synth window.speechSynthesis; synth.speak(utterThis);加入配置&#xff0c;可以配置语言、音量、语速、音高&#xff0c;继续玩&#xff1a; …...

呆头鹅-全自动视频混剪,批量剪辑批量剪视频,探店带货系统,精细化顺序混剪,故事影视解说,视频处理大全,精细化顺序混剪,多场景裂变,多视频混剪

视频闪闪-全自动视频混剪&#xff0c;探店带货系统&#xff0c;多视频混剪&#xff0c;让你成为视频处理大师&#xff01; 一、全自动视频混剪 www.shipinshanshan.com 你是否曾经厌烦于冗长的视频剪辑过程&#xff1f;是否曾经为了一个短短的混剪视频而熬夜加班&#xff1f;现…...

牛客竞赛网(爱吃素)

题目描述 牛妹是一个爱吃素的小女孩&#xff0c;所以很多素数都害怕被她吃掉。 一天&#xff0c;两个数字aaa和bbb为了防止被吃掉&#xff0c;决定和彼此相乘在一起&#xff0c;这样被吃掉的风险就会大大降低&#xff0c;但仍有一定的可能被吃掉&#xff0c;请你判断他们相乘后…...

基于高效多分支卷积神经网络的生长点精确检测与生态友好型除草

Eco-friendly weeding through precise detection ofgrowing points via efficient multi-branch convolutional neural networks 摘要1、介绍2、相关工作2.1 杂草检测,高效除草2.2 用于密集预测任务的编解码网络2.3 语义图形是一种有效的标签方法3、总结摘要 在本研究中,我…...

11月9日,每日信息差

今天是2023年11月09日&#xff0c;以下是为您准备的17条信息差 第一、中国电信在进博会上与诺基亚、爱立信、英特尔、戴尔、三星达成采购合作意向。采购范围涵盖无线、数据和传输、固网终端、服务器、CPU、手机终端等设备及服务 第二、马斯克称SpaceX明年将每两天发射一次火箭…...

什么是 eCPM?它与 CPM 有何不同?

目录 eCPM 什么是 eCPM&#xff1f;它与 CPM 有何不同&#xff1f; 如何计算 eCPM&#xff1f; 该指标的主要优势有哪些&#xff1f; eCPM 底价 eCPM 达到多少比较合适&#xff1f; eCPM 每千人有效成本 (eCPM) 是指发行商&#xff08;App 开发者&#xff09;在 App 中每…...

Power Automate-创建和运行

网站&#xff1a;Microsoft Power Automate 根据自己需求选择创建 选择需要的触发方式&#xff0c;以即时云端流为例&#xff0c;点击触发流 点击添加新步骤 可以选择多种微软应用或者自定义应用连接 此处以向SharePoint列表追加项为例&#xff0c;要提前创建好SharePoint列表…...

【STM32 开发】| INA219采集电压、电流值

目录 前言1 原理图2 IIC地址说明3 寄存器地址说明4 开始工作前配置5 程序代码1&#xff09;驱动程序2&#xff09;头文件3) 测试代码 前言 INA219 是一款具备 I2C 或 SMBUS 兼容接口的分流器和功率监测计。该器件监测分流器电压降和总线电源电压&#xff0c;转换次数和滤波选项…...

蓝桥杯每日一题203.11.7

题目描述 题目分析 使用dp思维&#xff0c;当前位置是否可行是有上一位置推来&#xff0c;计算出最大的可行位置即可 #include <stdio.h> #include <string.h>#define N 256 int f(const char* s1, const char* s2) {int a[N][N];int len1 strlen(s1);int len2 …...

ESP32建立TCP连接

ESP32建立TCP连接 1.搭建ESP-IDF开发环境 搭建开发环境直接从官网下载即可。 https://docs.espressif.com/projects/esp-idf/zh_CN/v5.1.1/esp32s3/index.html https://dl.espressif.com/dl/esp-idf/?idf4.4 使用官方的下载器下载好&#xff0c;就可以自动安装&#xff0…...

普华永道成功举办《国有基金高质量发展提效创效服务》主题分享活动,助力国有基金提效创效

上海&#xff0c;2023年11月10日 —今天普华永道在上海中国国际进口博览会&#xff08;以下简称“进博会”&#xff09;现场举办《为“国”出力&#xff0c;“普”实有“华”—— 普华永道国有基金高质量发展提效创效服务》主题分享活动&#xff0c;就国有基金监督、管理、投资…...

黑洞路由的几种应用场景

第一种在内网中产生环路&#xff1a; 这种核心交换机上肯定写一条默认路由 0.0.0.0 0 10.0.0.1 出口路由要写一条192.168.0.0 16 10.0.0.2 如果出口路由访问一条不存在的内网网段&#xff0c;又或者访问的那台终端停机了&#xff0c;那就会产生三层环路&#xff0c;数据包在…...

数据分析:智能企业七步曲(一)

原创&#xff1a; MicroStrategy微策略中国 作者&#xff1a;数据杰论 时间走到2018年最后一个季度&#xff0c;过去几年热炒的大数据概念正在各行各业开始落地并展开实际应用&#xff0c;核心是关注数据如何能为企业带来价值。因此&#xff0c;数据分析及其种种实现手段不断被…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...