问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

手把手教你SpringBoot集成SFTP客户端实现文件上传下载

发布网友 发布时间:2024-09-29 08:43

我来回答

1个回答

热心网友 时间:2024-10-30 06:17

背景

在项目开发中,一般文件存储很少再使用SFTP服务,但是也不排除合作伙伴使用SFTP来存储项目中的文件或者通过SFTP来实现文件数据的交互。我遇到的项目中,就有银行和保险公司等合作伙伴通过SFTP服务来实现与我们项目的文件数据的交互。

为了能够顺利地完成与友商的SFTP服务的连通,我们需要在自己的项目中实现一套SFTP客户端工具。一般我们会采用Jsch来实现SFTP客户端。

依赖<!--执行远程操作--><dependency><groupId>com.jcraft</groupId><artifactId>jsch</artifactId><version>0.1.55</version></dependency><!--链接池--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.11.1</version></dependency>

首先我们一定要引入jsch依赖,这个是我们实现SFTP客户端的基石;其次我们引入了链接池工具,为了避免每次执行SFTP命令都要重新创建链接,我们使用池化的方式优化了比较消耗资源的创建操作。

创建工具类

为了更好的使用SFTP工具,我们把jsch中关于SFTP的相关功能提炼出来,做了一次简单的封装,做成了我们可以直接使用的工具类。

里面只有两类方法:

1.创建Session与开启Session;

session创建好后,还不能创建channel,需要开启session后才能创建channel;

2.创建channel与开启channel;

channel也是一样,创建好的channel需要开启后才能真正地执行命令;

publicclassJschUtil{/***创建session**@paramuserName用户名*@parampassword密码*@paramhost域名*@paramport端口*@paramprivateKeyFile密钥文件*@parampassphrase口令*@return*@throwsAwesomeException*/publicstaticSessioncreateSession(StringuserName,Stringpassword,Stringhost,intport,StringprivateKeyFile,Stringpassphrase)throwsAwesomeException{returncreateSession(newJSch(),userName,password,host,port,privateKeyFile,passphrase);}/***创建session**@paramjSch*@paramuserName用户名*@parampassword密码*@paramhost域名*@paramport端口*@paramprivateKeyFile密钥*@parampassphrase口令*@return*@throwsAwesomeException*/publicstaticSessioncreateSession(JSchjSch,StringuserName,Stringpassword,Stringhost,intport,StringprivateKeyFile,Stringpassphrase)throwsAwesomeException{try{if(!StringUtils.isEmpty(privateKeyFile)){//使用密钥验证方式,密钥可以是有口令的密钥,也可以是没有口令的密钥if(!StringUtils.isEmpty(passphrase)){jSch.addIdentity(privateKeyFile,passphrase);}else{jSch.addIdentity(privateKeyFile);}}//获取sessionSessionsession=jSch.getSession(userName,host,port);if(!StringUtils.isEmpty(password)){session.setPassword(password);}//不校验域名session.setConfig("StrictHostKeyChecking","no");returnsession;}catch(Exceptione){thrownewAwesomeException(500,"createsessionfail");}}/***创建session**@paramjSch*@paramuserName用户名*@parampassword密码*@paramhost域名*@paramport端口*@return*@throwsAwesomeException*/publicstaticSessioncreateSession(JSchjSch,StringuserName,Stringpassword,Stringhost,intport)throwsAwesomeException{returncreateSession(jSch,userName,password,host,port,StringUtils.EMPTY,StringUtils.EMPTY);}/***创建session**@paramjSch*@paramuserName用户名*@paramhost域名*@paramport端口*@return*@throwsAwesomeException*/privateSessioncreateSession(JSchjSch,StringuserName,Stringhost,intport)throwsAwesomeException{returncreateSession(jSch,userName,StringUtils.EMPTY,host,port,StringUtils.EMPTY,StringUtils.EMPTY);}/***开启session链接**@paramjSch*@paramuserName用户名*@parampassword密码*@paramhost域名*@paramport端口*@paramprivateKeyFile密钥*@parampassphrase口令*@paramtimeout链接超时时间*@return*@throwsAwesomeException*/publicstaticSessionopenSession(JSchjSch,StringuserName,Stringpassword,Stringhost,intport,StringprivateKeyFile,Stringpassphrase,inttimeout)throwsAwesomeException{Sessionsession=createSession(jSch,userName,password,host,port,privateKeyFile,passphrase);try{if(timeout>=0){session.connect(timeout);}else{session.connect();}returnsession;}catch(Exceptione){thrownewAwesomeException(500,"sessionconnectfail");}}/***开启session链接**@paramuserName用户名*@parampassword密码*@paramhost域名*@paramport端口*@paramprivateKeyFile密钥*@parampassphrase口令*@paramtimeout链接超时时间*@return*@throwsAwesomeException*/publicstaticSessionopenSession(StringuserName,Stringpassword,Stringhost,intport,StringprivateKeyFile,Stringpassphrase,inttimeout)throwsAwesomeException{Sessionsession=createSession(userName,password,host,port,privateKeyFile,passphrase);try{if(timeout>=0){session.connect(timeout);}else{session.connect();}returnsession;}catch(Exceptione){thrownewAwesomeException(500,"sessionconnectfail");}}/***开启session链接**@paramjSch*@paramuserName用户名*@parampassword密码*@paramhost域名*@paramport端口*@paramtimeout链接超时时间*@return*@throwsAwesomeException*/publicstaticSessionopenSession(JSchjSch,StringuserName,Stringpassword,Stringhost,intport,inttimeout)throwsAwesomeException{returnopenSession(jSch,userName,password,host,port,StringUtils.EMPTY,StringUtils.EMPTY,timeout);}/***开启session链接**@paramuserName用户名*@parampassword密码*@paramhost域名*@paramport端口*@paramtimeout链接超时时间*@return*@throwsAwesomeException*/publicstaticSessionopenSession(StringuserName,Stringpassword,Stringhost,intport,inttimeout)throwsAwesomeException{returnopenSession(userName,password,host,port,StringUtils.EMPTY,StringUtils.EMPTY,timeout);}/***开启session链接**@paramjSch*@paramuserName用户名*@paramhost域名*@paramport端口*@paramtimeout链接超时时间*@return*@throwsAwesomeException*/publicstaticSessionopenSession(JSchjSch,StringuserName,Stringhost,intport,inttimeout)throwsAwesomeException{returnopenSession(jSch,userName,StringUtils.EMPTY,host,port,StringUtils.EMPTY,StringUtils.EMPTY,timeout);}/***开启session链接**@paramuserName用户名*@paramhost域名*@paramport端口*@paramtimeout链接超时时间*@return*@throwsAwesomeException*/publicstaticSessionopenSession(StringuserName,Stringhost,intport,inttimeout)throwsAwesomeException{returnopenSession(userName,StringUtils.EMPTY,host,port,StringUtils.EMPTY,StringUtils.EMPTY,timeout);}/***创建指定通道**@paramsession*@paramchannelType*@return*@throwsAwesomeException*/publicstaticChannelcreateChannel(Sessionsession,ChannelTypechannelType)throwsAwesomeException{try{if(!session.isConnected()){session.connect();}returnsession.openChannel(channelType.getValue());}catch(Exceptione){thrownewAwesomeException(500,"openchannelfail");}}/***创建sftp通道**@paramsession*@return*@throwsAwesomeException*/publicstaticChannelSftpcreateSftp(Sessionsession)throwsAwesomeException{return(ChannelSftp)createChannel(session,ChannelType.SFTP);}/***创建shell通道**@paramsession*@return*@throwsAwesomeException*/publicstaticChannelShellcreateShell(Sessionsession)throwsAwesomeException{return(ChannelShell)createChannel(session,ChannelType.SHELL);}/***开启通道**@paramsession*@paramchannelType*@paramtimeout*@return*@throwsAwesomeException*/publicstaticChannelopenChannel(Sessionsession,ChannelTypechannelType,inttimeout)throwsAwesomeException{Channelchannel=createChannel(session,channelType);try{if(timeout>=0){channel.connect(timeout);}else{channel.connect();}returnchannel;}catch(Exceptione){thrownewAwesomeException(500,"connectchannelfail");}}/***开启sftp通道**@paramsession*@paramtimeout*@return*@throwsAwesomeException*/publicstaticChannelSftpopenSftpChannel(Sessionsession,inttimeout)throwsAwesomeException{return(ChannelSftp)openChannel(session,ChannelType.SFTP,timeout);}/***开启shell通道**@paramsession*@paramtimeout*@return*@throwsAwesomeException*/publicstaticChannelShellopenShellChannel(Sessionsession,inttimeout)throwsAwesomeException{return(ChannelShell)openChannel(session,ChannelType.SHELL,timeout);}enumChannelType{SESSION("session"),SHELL("shell"),EXEC("exec"),X11("x11"),AGENT_FORWARDING("auth-agent@openssh.com"),DIRECT_TCPIP("direct-tcpip"),FORWARDED_TCPIP("forwarded-tcpip"),SFTP("sftp"),SUBSYSTEM("subsystem");privatefinalStringvalue;ChannelType(Stringvalue){this.value=value;}publicStringgetValue(){returnthis.value;}}}SFTP链接池化

我们通过实现BasePooledObjectFactory类来池化通道ChannelSftp。这并不是真正池化的代码,下面的代码只是告知池化管理器如何创建对象和销毁对象。

staticclassSftpFactoryextendsBasePooledObjectFactory<ChannelSftp>implementsAutoCloseable{privateSessionsession;privateSftpPropertiesproperties;//初始化SftpFactory//里面主要是创建目标session,后续可用通过这个session不断地创建ChannelSftp。SftpFactory(SftpPropertiesproperties)throwsAwesomeException{this.properties=properties;Stringusername=properties.getUsername();Stringpassword=properties.getPassword();Stringhost=properties.getHost();intport=properties.getPort();StringprivateKeyFile=properties.getPrivateKeyFile();Stringpassphrase=properties.getPassphrase();session=JschUtil.createSession(username,password,host,port,privateKeyFile,passphrase);}//销毁对象,主要是销毁ChannelSftp@OverridepublicvoiddestroyObject(PooledObject<ChannelSftp>p)throwsException{p.getObject().disconnect();}//创建对象ChannelSftp@OverridepublicChannelSftpcreate()throwsException{inttimeout=properties.getTimeout();returnJschUtil.openSftpChannel(this.session,timeout);}//包装创建出来的对象@OverridepublicPooledObject<ChannelSftp>wrap(ChannelSftpchannelSftp){returnnewDefaultPooledObject<>(channelSftp);}//验证对象是否可用@OverridepublicbooleanvalidateObject(PooledObject<ChannelSftp>p){returnp.getObject().isConnected();}//销毁资源,关闭session@Overridepublicvoidclose()throwsException{if(Objects.nonNull(session)){if(session.isConnected()){session.disconnect();}session=null;}}}

为了实现真正的池化操作,我们还需要以下代码:

1.我们需要在SftpClient对象中创建一个GenericObjectPool对象池,这个才是真正的池子,它负责创建和存储所有的对象。

2.我们还需要提供资源销毁的功能,也就是实现AutoCloseable,在服务停止时,需要把相关的资源销毁。

publicclassSftpClientimplementsAutoCloseable{privateSftpFactorysftpFactory;GenericObjectPool<ChannelSftp>objectPool;//构造方法1publicSftpClient(SftpPropertiesproperties,GenericObjectPoolConfig<ChannelSftp>poolConfig)throwsAwesomeException{this.sftpFactory=newSftpFactory(properties);objectPool=newGenericObjectPool<>(this.sftpFactory,poolConfig);}//构造方法2publicSftpClient(SftpPropertiesproperties)throwsAwesomeException{this.sftpFactory=newSftpFactory(properties);SftpProperties.PoolConfigconfig=properties.getPool();//默认池化配置if(Objects.isNull(config)){objectPool=newGenericObjectPool<>(this.sftpFactory);}else{//自定义池化配置GenericObjectPoolConfig<ChannelSftp>poolConfig=newGenericObjectPoolConfig<>();poolConfig.setMaxIdle(config.getMaxIdle());poolConfig.setMaxTotal(config.getMaxTotal());poolConfig.setMinIdle(config.getMinIdle());poolConfig.setTestOnBorrow(config.isTestOnBorrow());poolConfig.setTestOnCreate(config.isTestOnCreate());poolConfig.setTestOnReturn(config.isTestOnReturn());poolConfig.setTestWhileIdle(config.isTestWhileIdle());poolC
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
现在PS4上的DAI联机是不是没人玩了 古印度是什么 三星5360怎样下载91熊猫看书 如果手机刷新了版本怎样下载91熊猫看书? 91熊猫看书怎么从云书架下载书,谢谢 91熊猫看书 怎么安装 91熊猫看书怎么安装 吱哩哇啦是什么意思怎么读 柿子和面做饼怎么做 男人什么时候流行穿高跟鞋 新款奥迪A4L"不厚道"起售价上涨1.9万,看完配置表后发现很合理!_百度... 新人加入中脉后怎样才能最终走向辉煌(转载) vivox710f能破解上移动3g VIVOX710F手机有没有OGT功能 vivox710f怎样才能把场景桌面切换成经典桌面 请问汉字、平假名、片假名在日文中分别起什么作用,充当什么成分?缺少... 日语的片假名和平假名有什么区别?一般一个句子是写平假名还是片... 我是学公共事业管理的,2012考研,苏州大学的社会医学与卫生事业管理专业... 我是学公共事业管理的,想考研,是考心理学还是考本专业呢? 本人公共事业管理专业,介于考研选纯文科的比如本专业,法硕,政治学_百度... 我是公共事业管理专业的,考研不知道是考一科数学还是考两个专业课,那个... 我是公共事业管理的本科学生,我想考研,有些地方不明白求指导...谢谢... 现在学的是公共事业管理专业。打算考研,对本专业不是很干兴趣,不知道跨... 无主之地2新手适合什么角色 无主之地2最强职业推荐介绍_无主之地2最强职业推荐是什么 无主之地2哪个职业好玩 无主之地2哪个职业强 ...没有一个可以帮我,我只不过想结婚,让他们帮帮我都没有帮我的,全都... 请教不择人是什么意思? 怎么把手机电影下载到u盘上 科技安全包括什么? 科技安全的主要内容包括什么多选题 艾灸的正确方法 什么时候最好 无瘢痕灸操作方法 无瘢痕灸概述 如何利用针灸治疗 为什么我不能放心老公去参加聚会呢? 95508怎么转人工 广发 标致3008出现abs系统故障,esp系统故障,发动机故障 页面设置在哪里一键重装系统 社会保险经济学目录 猫咪喝舒化奶脱脂还是全脂好 与羊奶比哪个更好 洗衣机排水泵堵塞怎么处理啊? 北京哈尼K专业汽车音响改装怎么样? 猫上pon一直闪烁黄色,los不亮.连不上网,错误码是651 ...闪红灯,PON灯不亮,今天PON灯闪绿灯,LOS灯闪红灯,连不上网是怎么回 ... 我的火车票在网上买了在这里取不到票他叫我去火车站取票是怎么回事 美团怎么设置定时点餐 ...关于老鼠资料,能提供多少就提供多少 尽量不要用老鼠药。_百度... 如何防止家里的老鼠呢?