sftp做成一个池子
前言:开发中的需求要去对方的 ftp
服务器下载文件,这里下载文件采用 ftp
方式,下载之后程序再去解析文件的内容,然后再存数据库。下载过来的文件默认是 zip
格式,需要解压 unzip
一下,然后里面有一个 csv
文件,用 easyecel
读取 csv
中的数据,至此需求梳理完成。这里每次取下载或者操作 ftp
的时候都要新建一个连接,用完再主动关闭,这里并未复用此连接,相当于说每次都是新建一个连接,这样不好,因此对这里进行设计改造。
引入 pom 依赖
<!-- SFTP --><dependency><groupId>com.jcraft</groupId><artifactId>jsch</artifactId><version>0.1.55</version></dependency><!-- commons-pool2 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.6.1</version></dependency>
定义yml文件配置信息
sftp:mft:sftp-host: 101.233.389.58sftp-port: 80219sftp-username: usersftp-password: 789678sftp-path: /file/mftsave-path: /mftpool:max-total: 10max-idle: 10min-idle: 5
定义配置属性SftpProperties
信息
读取yml
中的属性信息,注意@ConfigurationProperties(prefix = "sftp.mft")
注解是定义属性,下面的
sftpHost
属性就读取到了yml
文件中配置的信息,但是类中的maxTotal
和yml文件中的max-total
是怎么映射的呢?注意看yml
中有一个pool
下面 类中有一个private Pool pool = new Pool();
把pool
改个名字就从yml
文件点不进来了,说明是根据此来映射的
package x.x.config.sftpPool;import com.jcraft.jsch.ChannelSftp;
import lombok.Data;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;@Data
@ConfigurationProperties(prefix = "sftp.mft")
public class SftpProperties {private String sftpHost;private int sftpPort;private String sftpUsername;private String sftpPassword;private String sftpPath;private String savePath;private Pool pool = new Pool();public static class Pool extends GenericObjectPoolConfig<ChannelSftp> {private int maxTotal = DEFAULT_MAX_TOTAL;private int maxIdle = DEFAULT_MAX_IDLE;private int minIdle = DEFAULT_MIN_IDLE;public Pool() {super();}@Overridepublic int getMaxTotal() {return maxTotal;}@Overridepublic void setMaxTotal(int maxTotal) {this.maxTotal = maxTotal;}@Overridepublic int getMaxIdle() {return maxIdle;}@Overridepublic void setMaxIdle(int maxIdle) {this.maxIdle = maxIdle;}@Overridepublic int getMinIdle() {return minIdle;}@Overridepublic void setMinIdle(int minIdle) {this.minIdle = minIdle;}}
}
定义工厂类
生产sftp
连接,获取上一步定义好的属性 SftpProperties
实现三个方法,create
方法创建连接
package x.x.config.sftpPool;import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;import java.util.Properties;@Data
@Slf4j
public class SftpFactory extends BasePooledObjectFactory<ChannelSftp> {private SftpProperties properties;public SftpFactory(SftpProperties properties) {this.properties = properties;}@Overridepublic ChannelSftp create() {try {JSch jsch = new JSch();Session sshSession = jsch.getSession(properties.getSftpUsername(), properties.getSftpHost(), properties.getSftpPort());sshSession.setPassword(properties.getSftpPassword());Properties sshConfig = new Properties();sshConfig.put("StrictHostKeyChecking", "no");sshSession.setConfig(sshConfig);sshSession.connect();ChannelSftp channel = (ChannelSftp) sshSession.openChannel("sftp");channel.connect();return channel;}catch (JSchException e){throw new RuntimeException("连接sfpt失败", e);}}@Overridepublic PooledObject<ChannelSftp> wrap(ChannelSftp channelSftp) {return new DefaultPooledObject<>(channelSftp);}@Overridepublic void destroyObject(PooledObject<ChannelSftp> p) throws Exception {ChannelSftp channelSftp = p.getObject();channelSftp.disconnect();}
}
定义池子
引入工厂制造,此处会根据yml
文件配置的属性生成连接
package x.x.config.sftpPool;import com.jcraft.jsch.ChannelSftp;
import lombok.Data;
import org.apache.commons.pool2.impl.GenericObjectPool;@Data
public class SftpPool {private GenericObjectPool<ChannelSftp> pool;public SftpPool(SftpFactory factory) {this.pool = new GenericObjectPool<>(factory, factory.getProperties().getPool());}/*** 获取一个sftp连接对象* @return sftp连接对象*/public ChannelSftp borrowObject() {try {return pool.borrowObject();} catch (Exception e) {throw new RuntimeException("获取ftp连接失败", e);}}/*** 归还一个sftp连接对象* @param channelSftp sftp连接对象*/public void returnObject(ChannelSftp channelSftp) {if (channelSftp!=null) {pool.returnObject(channelSftp);}}
}
定义help类操作
引入pool
直接操作获取,用完返回
package x.x.config.sftpPool;import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;
import lombok.extern.slf4j.Slf4j;import java.io.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;@Slf4j
public class SftpHelper {private SftpPool pool;public SftpHelper(SftpPool pool) {this.pool = pool;}public void createDir(String createPath) throws Exception {ChannelSftp channelSftp = pool.borrowObject();try {channelSftp.cd("/");if (!isDirExist(createPath)) {String[] pathArry = createPath.split("/");for (String path : pathArry) {if ("".equals(path)) {continue;}if (isDirExist(path)) {channelSftp.cd(path);} else {// 建立目录channelSftp.mkdir(path);// 进入并设置为当前目录channelSftp.cd(path);}}} else {channelSftp.cd(createPath);}} catch (SftpException e) {log.error(e.getMessage(), e);throw new Exception("创建路径错误:" + createPath);}finally {pool.returnObject(channelSftp);}}public boolean isDirExist(String directory) {ChannelSftp channelSftp = pool.borrowObject();boolean isDirExistFlag = false;try {channelSftp.cd("/");SftpATTRS sftpATTRS = channelSftp.lstat(directory);isDirExistFlag = true;channelSftp.cd(directory);return sftpATTRS.isDir();} catch (Exception e) {log.error(e.getMessage(), e);if ("no such file".equals(e.getMessage().toLowerCase())) {isDirExistFlag = false;}}finally {pool.returnObject(channelSftp);}return isDirExistFlag;}/*** 文件上传** @param directory 目录* @param fileName 要上传的文件名*/public void uploadFile(ChannelSftp uploadChannelSftp, String directory, String sourceFilePath, String fileName) {ChannelSftp channelSftp = pool.borrowObject();//获取输入流InputStream inputStream = null;try {inputStream = uploadChannelSftp.get(sourceFilePath);} catch (SftpException e) {e.printStackTrace();throw new RuntimeException(e);}finally {pool.returnObject(channelSftp);}try {channelSftp.cd(directory);//写入文件channelSftp.put(inputStream, fileName);} catch (Exception e) {e.printStackTrace();throw new RuntimeException("文件上传异常:" + e.getMessage());} finally {pool.returnObject(channelSftp);if (inputStream != null) {try {inputStream.close();} catch (IOException e) {throw new RuntimeException("Close stream error." + e.getMessage());}}}}/*** 列出目下下的所有文件** @param sftpPath 文件路径* @return 文件名* @throws Exception 异常*/public List<String> listFiles(String sftpPath) {ChannelSftp channelSftp = pool.borrowObject();Vector fileList = null;List<String> fileNameList = new ArrayList<>();try {fileList = channelSftp.ls(sftpPath);} catch (SftpException e) {throw new RuntimeException(e);}Iterator it = fileList.iterator();while (it.hasNext()) {String fileName = ((ChannelSftp.LsEntry) it.next()).getFilename();if (".".equals(fileName) || "..".equals(fileName)) {continue;}fileNameList.add(fileName);}pool.returnObject(channelSftp);return fileNameList;}/*** 下载文件。** @param sourceDirectory 下载目录* @param downloadFile 下载的文件* @param targetSaveDirectory 存在本地的路径*/public void downloadFile(String sourceDirectory, String downloadFile, String targetSaveDirectory) throws RuntimeException {ChannelSftp channelSftp = pool.borrowObject();OutputStream targetFile = null;try {if (sourceDirectory != null && !"".equals(sourceDirectory)) {channelSftp.cd(sourceDirectory);}File folder = new File(targetSaveDirectory);// 确保文件夹存在if (!folder.exists()) {if (!folder.mkdirs()) {log.error("无法创建文件夹");return;}}targetFile = new FileOutputStream(new File(targetSaveDirectory, downloadFile));channelSftp.get(downloadFile, targetFile);} catch (Exception e) {throw new RuntimeException(e);} finally {pool.returnObject(channelSftp);try {targetFile.close();} catch (IOException e) {throw new RuntimeException(e);}}}/*** 删除文件** @param directory 要删除文件所在目录* @param deleteFile 要删除的文件*/public void deleteFile(String directory, String deleteFile) throws Exception {ChannelSftp channelSftp = pool.borrowObject();try {channelSftp.cd(directory);channelSftp.rm(deleteFile);} catch (Exception e) {throw new Exception("文件删除失败" + e.getMessage());}finally {pool.returnObject(channelSftp);}}/*** 检查文件路径是否存在** @param path 文件路径* @return true/false*/public boolean checkedFileDirectory(String path) {ChannelSftp channelSftp = pool.borrowObject();File file = new File(path);if (!file.exists() && !file.isDirectory()) {log.info("file or dir not exist, will create it : "+ path);file.setWritable(true, false);return file.mkdirs();}pool.returnObject(channelSftp);return true;}
}
配置bean信息
SftpFactory
工厂的建立依赖于SftpProperties
配置属性,SftpProperties
是通过注解@EnableConfigurationProperties
引入
SftpPool
池子依赖于工厂SftpFactory
创建连接
sftpHelper
操作类依赖于 池子SftpPool
中的连接操作具体的文件
package x.x.config.sftpPool;import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
@EnableConfigurationProperties(SftpProperties.class)
public class SftpConfig {// 工厂@Beanpublic SftpFactory sftpFactory(SftpProperties properties) {return new SftpFactory(properties);}// 连接池@Beanpublic SftpPool sftpPool(SftpFactory sftpFactory) {return new SftpPool(sftpFactory);}// 辅助类@Beanpublic SftpHelper sftpHelper(SftpPool sftpPool) {return new SftpHelper(sftpPool);}
}
至此一个简单的池子就完成了
相关文章:
sftp做成一个池子
前言:开发中的需求要去对方的 ftp 服务器下载文件,这里下载文件采用 ftp 方式,下载之后程序再去解析文件的内容,然后再存数据库。下载过来的文件默认是 zip 格式,需要解压 unzip 一下,然后里面有一个 csv 文…...
全网最全-Netty从入门到精通
XiaoYongCai/2024/8/6 一:Netty入门 1.Netty概述 A.Netty的定义 Netty是一个提供异步事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。在Java领域,Netty被认为是除了Java原生NIO之外的最佳网络…...

C#知识|文件与目录操作:对象的创建、保存、读取
哈喽,你好啊,我是雷工! 面向对象编程的特点就是一切皆对象,操作的也是对象,本节学习文件与目录操作中,对象的保存; 以下为学习笔记。 01 对象的特点 ①:对象运行在内存中ÿ…...

自定义 SwiftUI 中符号图像的外观
文章目录 前言大小颜色渲染模式单色分层调色板多色 可变值设计变体示例代码结论 前言 符号图像是来自 Apple的SF Symbols 库的矢量图标,设计用于在 Apple 平台上使用。这些可缩放的图像适应不同的大小和重量,确保在我们的应用程序中具有一致的高质量图标…...

循环神经网络和自然语言处理一
目录 一.分词 1.分词工具 2.分词的方法 3.N-gram表示方法 二.向量化 1.one-hot编码 2.word embedding 3.word embedding API 4.数据形状改变 既然是自然语言,那么就有字,词,句了 一.分词 1.分词工具 tokenization,jie…...

CSS技巧专栏:一日一例 20-纯CSS实现点击会凹陷的按钮
本例图片 案例分析 其实这个按钮非常的简单啊,主要就是利用了box-shadow的inset。 布局代码 <button class="base">凹下的按钮</button> 基础样式 :root{--main-bg-color: #dcdcdc; /* 将页面背景色调整为浅灰色 */--color:#000;--hover-color:#99…...

20240807 每日AI必读资讯
👨💼马斯克再发难、OpenAI 高层巨变:两大核心人物离职,总裁休长假到年底 - OpenAI 联合创始人 John Schulman 官宣离职,加入原是竞品公司的 Anthropic - 陪伴 OpenAI 共同成长 9 年的总裁兼联合创始人 Greg Brockm…...

海外社媒账号如何让防关联?账号隔离的5大要点
在跨境电商迅速发展和全球化营销的背景下,海外社交媒体平台成为外贸人拓展市场的关键阵地。因此,为了保障账号安全,实现高效推广,账号隔离以及安全防关联对外贸人来说至关重要。本文将盘点引起海外社媒账号关联的原因及其五大解决…...

下一代 AI 搜索引擎 MindSearch:多智能体 + 系统2,模拟人类认知过程的 AI 搜索引擎
下一代 AI 搜索引擎 MindSearch:多智能体 系统2,模拟人类认知过程的 AI 搜索引擎 提出背景解法拆解实验评估开放集封闭集问答 论文大纲怎么进一步改进 MindSearch?1. 组合(Combination)2. 拆开(Disassembl…...

一键生成专业PPT:2024年AI技术在PPT软件中的应用
不知道你毕业答辩的时候有没有做过PPT,是不是也被这个工具折磨过。没想到现在都有AI生成PPT的工具了吧?这次我就介绍几款可以轻松生成PPT的AI工具吧。 1.笔灵AIPPT 连接直达:https://ibiling.cn/ppt-zone 这个工具我最早是…...

Godot学习笔记8——PONG游戏制作
目录 一、小球 二、地图 三、积分系统 四、玩家场景 五、导出与发布 PONG是1972年由雅达利公司推出的游戏,主要玩法为玩家控制两个可以上下移动的板子击打屏幕中不断运动的球 一、小球 我们首先创建一个“Area2D”场景,在它下方创建“Collisi…...

软件RAID配置实战(2个案例场景)
文章目录 3、软件RAID管理-mdadm工具安装mdadm组件格式示例选项说明mdadm命令其它常用选项 4、相关查询命令查看创建RAID的进度查看RAID磁盘详细信息查看文件系统的磁盘空间使用情况 5、RAID配置示例场景1:RAID5步骤 场景2:RAID10步骤 6、移除RAID阵列 接…...

# 基于MongoDB实现商品管理系统(2)
基于MongoDB实现商品管理系统(2) 基于 mongodb 实现商品管理系统之准备工作 1、案例需求 这里使用的不是前端页面,而是控制台来完成的。 具体的需求如下所示: 运行 查询所有 通过id查询详情 添加 - 通过id删除 2、案例分析 程…...

国标GB28181视频平台LntonCVS视频融合共享平台视频汇聚应用方案
近年来,国内视频监控应用迅猛发展,系统接入规模不断扩大,导致了大量平台提供商的涌现。然而,不同平台的接入协议千差万别,使得终端制造商不得不为每款设备维护多个不同平台的软件版本,造成了资源的严重浪费…...
java基础I/O
1,I/O流的概念: IO流代表的是一个数据输入的源或者输出的目标地址,可以是硬盘,内存,网络或者什么其他的电子设备,而IO流的类型也很多比如最简单的字节或者字符,或者其他更高级的对象。 不管它有多少特性&am…...
关于“八股文”在程序员面试中的角色及其对工作实际效用的讨论
关于“八股文”在程序员面试中的角色及其对工作实际效用的讨论,确实是一个值得深入探讨的话题。这里,“八股文”通常指的是面试中常见的一系列标准化问题和答案,涵盖了编程语言基础、算法、数据结构、设计模式、框架知识等,这些内…...

【算法设计题】基于front、rear和count的循环队列初始化、入队和出队操作,第6题(C/C++)
目录 第3题 基于front、rear和count的循环队列初始化、入队和出队操作 得分点(必背) 题解:基于front、rear和count的循环队列初始化、入队和出队操作 数据结构定义 代码解答 详细解释 1. 循环队列初始化 2. 循环队列入队 3. 循环队列…...

端点区间影响
前言:这一题本来想就是直接来一个前缀和来写,直接左边加一,右边减一,但是细想好像有问题,我们平时做的题目左边端点造成的影响会对这一段区间造成影响,但是这一题的话超过了左边端点就不会有影响了 那这一题…...

Leetcode3224. 使差值相等的最少数组改动次数
Every day a Leetcode 题目来源:3224. 使差值相等的最少数组改动次数 解法1: 想一想,什么情况下答案是 0?什么情况下答案是 1? 如果答案是 0,意味着所有 ∣nums[i]−nums[n−1−i]∣ 都等于同一个数 X。…...

thinkphp之命令执行漏洞复现
实战: fofa搜索thinkphp-- 第一步:先在dns平台上,点击Get SubDomain ,监控我们的注入效果 返回dnslog查看到了Java的版本信息 打开kali监听端口 进行base64编码 bash -i >& /dev/tcp/192.168.189.150/8080 0>&1 …...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...

云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...

【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...

云安全与网络安全:核心区别与协同作用解析
在数字化转型的浪潮中,云安全与网络安全作为信息安全的两大支柱,常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异,并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全:聚焦于保…...

EasyRTC音视频实时通话功能在WebRTC与智能硬件整合中的应用与优势
一、WebRTC与智能硬件整合趋势 随着物联网和实时通信需求的爆发式增长,WebRTC作为开源实时通信技术,为浏览器与移动应用提供免插件的音视频通信能力,在智能硬件领域的融合应用已成必然趋势。智能硬件不再局限于单一功能,对实时…...