目前在国内项目开发中,使用Spring Integration技术的比较少,尤其是中文的参考文献和项目案例,更是罕有。鉴于此,本文详细介绍spring integration sftp模块在Sftp服务器和本地服务器之间文件的传送。
SFTP(Secure File Transfer Protocol) 安全文件传送协议,允许文件通过流的形式在网络之间进行传输。文件传输的过程涉及到两个重要的因素,安全渠道(secure channel,如SSH)以及SFTP联接身份的识别(SFTP session)。Spring Integration提供三种方式来支持文件在SFTP服务器的发送和接收:Inbound Channel Adapter,Outbound Channel Adapter,Outbound Gateway。
JSch支持在一个连接配置上多个channel的操作。原生的JSch技术开发,在打开一个channel操作之前,需要建立Session的连接。同样的,默认情况,Spring Integration为每一个channel操作使用单独的物理连接。在3.0版本发布之后,Cache Session Factory 出现 (CachingSessionFactory),将Session Factory包装在缓存中,支持Session共享,可以在一个连接上支持多个JSch Channel的操作。如果缓存被重置,在最后一次channel关闭之后,才会断开连接。
下面是XML方式定义的Session Factory的bean:
<beans:bean id="sftpSessionFactory"
class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<beans:property name="host" value="localhost"/>
<beans:property name="privateKey" value="classpath:META-INF/keys/sftpTest"/>
<beans:property name="privateKeyPassphrase" value="springIntegration"/>
<beans:property name="port" value="22"/>
<beans:property name="user" value="songhj"/>
</beans:bean>
下面是使用springboot方式定义的Session Factory:
@Bean
public SessionFactory<LsEntry> sftpSessionFactory(){
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
factory.setUser("songhj");
factory.setHost("127.0.0.1");
factory.setPort("22");
factory.setPassword("password");
return factory;
}
<bean id="sftpSessionFactory"
class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="localhost"/>
</bean>
<bean id="cachingSessionFactory"
class="org.springframework.integration.file.remote.session.CachingSessionFactory">
<constructor-arg ref="sftpSessionFactory"/>
<property name="sessionCacheSize" value="10"/>
<property name="sessionWaitTimeout" value="1000"/>
</bean>
从Spring Integration version 3.0开始,CachingConnectionFactory提供了resetCache()方法。当被调用时,所有空闲会话将立即关闭,而正在使用的会话将在返回到缓存时关闭。当使用isSharedSession=true时,通道是关闭的,而共享会话仅在最后一个通道关闭时才关闭。对会话的新请求将根据需要建立新的会话。
<int-sftp:inbound-channel-adapter id="sftpAdapterAutoCreate"
session-factory="sftpSessionFactory"
channel="requestChannel"
filename-pattern="*.txt"
remote-directory="/foo/bar"
preserve-timestamp="true"
local-directory="file:target/foo"
auto-create-local-directory="true"
local-filename-generator-expression="#this.toUpperCase() + '.a'"
local-filter="myFilter"
delete-remote-files="false">
<int:poller fixed-rate="1000"/>
</int-sftp:inbound-channel-adapter>
从上面的配置可以理解,由inbound-channel-adapter元素创建,同时提供各种属性的值,local-directory:文件被转移的目标文件夹;remote-directory:文件来源远程文件夹目录;session-factory:session 会话工厂;channel:需要的渠道;文件过滤器以及是否删除转移后的文件等属性。
下面是springboot方式配置的Sftp Inbound channel adapter:
@Bean
@InboundChannelAdapter(value = "inboundFileChannel",
poller = @Poller(cron = "1/10 * * * * *", maxMessagesPerPoll = "1"))
public MessageSource<File> fileMessageSource() {
//创建sftpInboundFileSynchronizer,并绑定到message source.
SftpInboundFileSynchronizingMessageSource source =
new SftpInboundFileSynchronizingMessageSource(sftpInboundFileSynchronizer());
//自动创建本地文件夹
source.setAutoCreateLocalDirectory(true);
source.setLocalDirectory(new File(sftpProperty.getLocalTempDir()));
//设置文件过滤器
source.setLocalFilter(new AcceptOnceFileListFilter<File>());
return source;
}
/**
* 为Inbound-channel-adapter提供bean
*/
@Bean
public DirectChannel inboundFileChannel() {
return new DirectChannel();
}
/**
* SftpInboundFileSynchronizer,
*
* 同步sftp文件至本地服务器.
* <1> 可以放在service中获取bean使用.toLocal方法;
* <2> 也可以使用inbound-channel-adapter中,做监控文件服务器的动态。
*
* @return SftpInboundFileSynchronizer
*/
@Bean(name = "synFileChannel")
public SftpInboundFileSynchronizer sftpInboundFileSynchronizer (){
SftpInboundFileSynchronizer fileSynchronize =
new SftpInboundFileSynchronizer(sftpSessionFactory());
fileSynchronize.setDeleteRemoteFiles(true);
fileSynchronize.setPreserveTimestamp(true);
//!important
fileSynchronize.setRemoteDirectory(sftpProperty.getSftpSendPath());
fileSynchronize.setFilter(new SftpSimplePatternFileListFilter("*.*"));
//fileSynchronize.setLocalFilenameGeneratorExpression( );
fileSynchronize.setPreserveTimestamp(true);
return fileSynchronize;
}
文件名设置:默认情况下,传输的文件将与原始文件具有相同的名称。Inbound channel adapter提供local-filename-generator- Expression属性,允许提供SpEL表达式来生成本地文件的名称。
文件修改时间戳:从Spring Integration 3.0开始,reserve-timestamp属性(默认为false);设置为true时,本地文件修改后的时间戳将设置为从服务器接收到的时间;否则将设置为当前时间。
文件过滤器:filename-pattern属性指定了简单的文件过滤模式。同时还提供了filename-regex属性来指定使用正则表达式(例如filename-regex=".*.test$")来过滤文件,以确定检索哪些远程文件。除了上面的过滤器之外,spring integration还提供了其他筛选器,如AcceptOnceFileListFilter,CompositeFileListFilter,SftpPersistentAcceptOnceFileListFilter,前者的作用是避免同步以前获取的文件。
下面是基于xml格式配置:
<int-sftp:outbound-channel-adapter id="sftpOutboundAdapter"
session-factory="sftpSessionFactory"
channel="inputChannel"
charset="UTF-8"
remote-directory="foo/bar"
remote-filename-generator-expression="payload.getName() + '-foo'"/>
由上面的配置,可以看到outbound channel adapter的属性;可以动态计算文件名或现有目录路径的表达式。目前Spring Integration并没有提供单独的注解来配置此adapter,可以使用@ServiceActivator 定义bean,来实现该配置。
下面是基于Spring boot框架配置,配置了Inbound Channel Adapter。
@SpringBootApplication
public class SftpJavaApplication {
@Bean
public SessionFactory<LsEntry> sftpSessionFactory() {
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
factory.setHost("localhost");
factory.setPort(port);
factory.setUser("foo");
factory.setPassword("foo");
factory.setAllowUnknownKeys(true);
factory.setTestSession(true);
return new CachingSessionFactory<LsEntry>(factory);
}
@Bean
public SftpInboundFileSynchronizer sftpInboundFileSynchronizer() {
SftpInboundFileSynchronizer fileSynchronizer = new SftpInboundFileSynchronizer(sftpSessionFactory());
fileSynchronizer.setDeleteRemoteFiles(false);
fileSynchronizer.setRemoteDirectory("foo");
fileSynchronizer.setFilter(new SftpSimplePatternFileListFilter("*.xml"));
return fileSynchronizer;
}
//配置Sftp Inbound Channel Adapter.
//Inbound Channel Adapter实质就是一个服务监控器,监控sftp上服务器上文件的创建。
@Bean
@InboundChannelAdapter(channel = "sftpChannel", poller = @Poller(fixedDelay = "5000"))
public MessageSource<File> sftpMessageSource() {
SftpInboundFileSynchronizingMessageSource source =
new SftpInboundFileSynchronizingMessageSource(sftpInboundFileSynchronizer());
source.setLocalDirectory(new File("sftp-inbound"));
source.setAutoCreateLocalDirectory(true);
source.setLocalFilter(new AcceptOnceFileListFilter<File>());
source.setMaxFetchSize(1);
return source;
}
//配置Sftp Outbound Channel Adapter.
//Outbound Channel Adapter实质上就是一个MessageHandler,接收发送的消息。
@Bean
@ServiceActivator(inputChannel = "sftpChannel")
public MessageHandler handler() {
return new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
System.out.println(message.getPayload());
}
};
}
}
Channel Adapter*
管道适配器,其就是一个端点Endpoint,顾名思义,就是用来适配连接Message channel与其他系统的。Channel Adapter分为inbound和outbound。刚一开始接触spring-integration时对这两个概念的非常不理解,在这里可以简单化,以当前的项目spring-integration为基准,所谓inbound就是从其他系统接收Message,获取资源,如sftp,file,Http等。而outbound就是将资源通过消息管道,发送资源到target目标服务器。
Service activator调用某个服务的操作来处理消息,如在下面的发送文件到sftp服务,就是将SftpMessageHandler来处理channel中的消息。
参考文献: