本篇博文介绍spring integration sftp技术中的sftp outbound gateway相关内容。Sftp outbound gateway 其实质就是提供一组命令(如图1)来实现对服务器上文件的交互操作,包括文件的获取(文件对象和文件名等)、上传(单文件和多文件)、下载(单文件和多文件),删除,移动。具体在开发的过程中可以使用多种配置方式如xml,springboot等。本文在介绍SFTP Outbound Gateway 的基础上,使用SpringBoot开发框架进行相应的开发实践。
1.命令组
1.1 ls
该命令的功能是获取远程文件,包括文件对象和文件路径名称等,具体返回值根据配置的选项:
- -1 :获取一组远程文件的文件名;默认是获取一组FileInfo对象;
- -a:获取所有的文件(包括开始的文件,递归时使用);
- - f:检索结果不用排序;
- -dirs: 包括文件夹,默认是包括的;
- -links:包括链接符号,默认是包括的;
- -R:递归方式获取远程文件夹下所有文件,默认不递归的。
除此之外,还可以配置文件名过滤器等;
命令返回值: 通过ls命令获取的message payload,是一组文件名或者FileInfo对象,对象中提供了有关文件的修改时间,权限以及其他的信息;
ls命令作用的远程文件夹,由header头的file_remoteDirectory属性提供;
建议提醒:如果使用-R递归选择项,文件名将含有子文件夹,表明递归文件的相对路径;如果使用-dirs选项,每一个递归的子文件夹,返回的元素中将含有子文件夹名;在这种情况下,建议不用使用-1罗列文件名,因为返回的元素中不能够区分是文件还是文件夹?建议返回FileInfo对象。
下面是开发示例:
@Bean
@ServiceActivator(inputChannel = "sftpChannel2")
public MessageHandler handler2() {
//指定session配置和命令
SftpOutboundGateway sftpOutboundGateway = new SftpOutboundGateway(sftpSessionFactory(),"ls","payload");
sftpOutboundGateway.setOptions("-dirs"); //配置项
return sftpOutboundGateway;
}
//使用Gateway触发
@MessagingGateway
public interface MessageGateway {
@Gateway(requestChannel = "sftpChannel2")
List<FileInfo> listFileName(String dir); //指定远程文件夹
}
1.2 nlst
该命令提供检索远程文件名的功能,相当于ls -1的命令;支持如下配置:
- -f:文件名不排序;
nlst命令作用的远程文件夹,由header头的file_remoteDirectory提供。
返回值:通过nlst获取的文件message payload,就是一组文件名列表;
1.3 get
该命令由于获取一个远程的文件,支持如下的选项:
- -P:文件下载之后,保持文件在本地的时间戳同远程服务器一致;
- -stream:以流的方式获取远程文件;
- -D:文件成功转移之后,删除远程文件;如果FileExistsMode设置为IGNORE,远程文件不会删除。
file_remoteDirectory 头包含了文件的远程路径,file_remoteFile属性为文件名;
返回值:使用get方法获取的message的payload是一个File对象,如果使用-straem,则payload就是一个InputStream文件流。
对于文本文件,有个通用的案例,使用file splitter 或 stream transformer。当以文件流的形式获取远程文件,Session在结束之后要及时关闭. Session由closeableResource属性header头文件,IntegrationMessageHeaderAccessor提供了流资源的关闭操作。
1.4 mget
该命令用来基于特定的文件模式过滤器获取多个文件,支持如下的设置:
- -P:保留远程文件的时间戳;
- -R:递归下载所有符合的文件;
- -x:没有文件匹配文件筛选模式,抛出异常,并返回空集合;
- -D:文件成功转移之后。如何FileExistsMode=IGNORE,本地文件存在,文件不会删除;
message payload返回的是List< >对象,集合元素是File。
注意:
在5.0版本之后,若FileExistsMode=IGNORE,payload不再包含已经存在的文件对象。
remote path的表达式应该是以结尾,类似myfiles/,表示获取完整的文件夹树myfiles;
注意,在版本5.0之后,MGET命令可以设置FileExistsMode.REPLACE_IF_MODIFIED模式,去同步整个文件夹,被修改的文件的时间戳也会相应修改。不用关心-P模式;
-R模式,默认情况下是整个文件夹,同时也支持设置文件或文件夹过滤器FileListFilter; 该过滤器提供两种方式filename-pattern或者filename-regex属性;例如filename-regex="(subDir|.*1.txt)" 获取subDir下所有以1.txt结尾的文件;
通常,将在local-directory-expression中使用#remoteDirectory变量,以便远程目录结构在本地保留。
下面是开发示例:
@Bean
@ServiceActivator(inputChannel = "sftpChannel3")
public MessageHandler handler3() {
SftpOutboundGateway sftpOutboundGateway = new SftpOutboundGateway(sftpSessionFactory(),"mget","payload");
sftpOutboundGateway.setOptions("-R");
sftpOutboundGateway.setFileExistsMode(FileExistsMode.REPLACE_IF_MODIFIED);
sftpOutboundGateway.setLocalDirectory(new File("E:\\sftp_tmp_dir"));
sftpOutboundGateway.setAutoCreateLocalDirectory(true);
return sftpOutboundGateway;
}
@MessagingGateway
public interface MessageGateway {
@Gateway(requestChannel = "sftpChannel3")
List<File> listFile(String dir);
}
1.5 put
该命令是发送单个文件到远程服务器;
message的payload可以是File对象,byte[]数组,或者字符串;
remote-filename-generator用来命名远程文件。其他的属性如remote-directory,temporary-remote-directory等等;
返回值:put命令的message的payload的返回值是string,包含文件传输后在服务器上的整个路径;
1.6 mput
该命令是发送多个文件到服务器,支持如下配置:
- -R: 递归发送文件和子文件夹下的所有文件;
message payload必须是文件或者文件路径字符串,代表了本地文件夹;自版本5.1之后,也支持文件或者路径字符串集合;
put的配置,同样适合mput,同时除此之外,还提供过滤文件的mput-pattern,mput-regex,mput-filter等;
版本4.3之后,支持设置文件的权限;
返回值:mput执行之后的返回值,是一个List,包含文件转移之后的路径集合。
下面是开发示例:
//!important,put命令需要借助与sftpRemoteFileTemplate。
//看源码,可以发现outbound gateway 有多种构造函数;
@Bean
@ServiceActivator(inputChannel = "sftpChannel4")
public MessageHandler handler4(){
SftpRemoteFileTemplate sftpRemoteFileTemplate = new SftpRemoteFileTemplate(sftpSessionFactory());
sftpRemoteFileTemplate.setRemoteDirectoryExpression(new LiteralExpression("/send"));
SftpOutboundGateway sftpOutboundGateway = new SftpOutboundGateway(sftpRemoteFileTemplate,"put","payload");
sftpOutboundGateway.setBeanFactory(beanFactory);
return sftpOutboundGateway;
}
@Bean
@ServiceActivator(inputChannel = "sftpChannel5")
public MessageHandler handler5(){
SftpRemoteFileTemplate sftpRemoteFileTemplate = new SftpRemoteFileTemplate(sftpSessionFactory());
sftpRemoteFileTemplate.setRemoteDirectoryExpression(new LiteralExpression("/send"));
SftpOutboundGateway sftpOutboundGateway = new SftpOutboundGateway(sftpRemoteFileTemplate,"mput","payload");
//配置过滤器
sftpOutboundGateway.setMputFilter(new FileListFilter<File>() {
@Override
public List<File> filterFiles(File[] files) {
if(...){
...
}
return null;
}
});
sftpOutboundGateway.setBeanFactory(beanFactory);
return sftpOutboundGateway;
}
1.7 rm
该命令是删除远程文件。
如果删除成功,message payload的返回值是Boolean.TRUE;否则是Boolean.FALSE。
file_remoteDirectory头包含远程文件属性;
下面是开发示例:
@Bean
@ServiceActivator(inputChannel = "sftpChannel6")
public MessageHandler handler6(){
SftpOutboundGateway sftpOutboundGateway = new SftpOutboundGateway(sftpSessionFactory(),"rm","payload");
sftpOutboundGateway.setBeanFactory(beanFactory);
return sftpOutboundGateway;
}
1.8 mv
该命令是移动文件在远程服务器上的位置。
返回值:转移成功,返回true,否则是false;
下面是开发示例:
@Bean
@ServiceActivator(inputChannel = "sftpChannel7")
public MessageHandler handler7(){
SftpOutboundGateway sftpOutboundGateway = new SftpOutboundGateway(sftpSessionFactory(),"mv","'send/22.TXT'");
sftpOutboundGateway.setRenameExpression(new LiteralExpression("send1/22.TXT"));
sftpOutboundGateway.setBeanFactory(beanFactory);
return sftpOutboundGateway;
}
以下是干货(测试用例):
首先是POM文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.flower.springintegration</groupId>
<artifactId>spring-integration-samples</artifactId>
<version>v0.0.1</version>
<name>SpringIntegrationExamples</name>
<description>Spring Integration Samples</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>-->
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-sftp</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>1.4.0.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.2.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
接下来是yml文件配置:
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://localhost:3306/springbatchexample?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
username: root
password: root
sftp:
host: 127.0.0.1
port: 23
user: 47gamer
password: wdnmd
filePath:
send: /send
achieve: /achieve
localPath: /sftp_tmp_dir
然后是Sftp网关配置类SftpConfig.java
package com.flower.integration.sftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.integration.annotation.*;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.file.filters.AcceptOnceFileListFilter;
import org.springframework.integration.file.filters.FileListFilter;
import org.springframework.integration.file.remote.FileInfo;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.file.support.FileExistsMode;
import org.springframework.integration.sftp.filters.SftpSimplePatternFileListFilter;
import org.springframework.integration.sftp.gateway.SftpOutboundGateway;
import org.springframework.integration.sftp.inbound.SftpInboundFileSynchronizer;
import org.springframework.integration.sftp.inbound.SftpInboundFileSynchronizingMessageSource;
import org.springframework.integration.sftp.outbound.SftpMessageHandler;
import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
import org.springframework.integration.sftp.session.SftpRemoteFileTemplate;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import javax.annotation.Resource;
import java.io.File;
import java.util.List;
import java.util.Properties;
/**
* Sftp configuration.
*
* @Autor 47Gamer
* @Date 2019-01-18
*/
@Configuration
@DependsOn("sftpProperty")
public class SftpConfig {
@Resource(name = "sftpProperty")
private SftpProperty sftpProperty;
private static Logger log = LoggerFactory.getLogger(SftpConfig.class);
@Value("${sftp.host}")
private String sftpHost;
@Value("${sftp.port:23}")
private int sftpPort;
@Value("${sftp.user}")
private String sftpUser;
@Value("${sftp.privateKey:#{null}}")
private org.springframework.core.io.Resource sftpPrivateKey;
@Value("${sftp.privateKeyPassphrase:}")
private String sftpPrivateKeyPassphrase;
@Value("${sftp.password}")
private String sftpPassword;
/* @Bean
public SessionFactory<LsEntry> sftpSessionFactory() {
System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(false);
factory.setHost(sftpProperty.getHost());
factory.setPort(sftpProperty.getPort());
factory.setUser(sftpProperty.getUser());
Properties jschProps = new Properties();
//!important 必须配置PreferredAuthentications,否则程序控制台会询问user name 和 password。
jschProps.put("StrictHostKeyChecking", "no");
jschProps.put("PreferredAuthentications",
"password,gssapi-with-mic,publickey,keyboard-interactive");
factory.setSessionConfig(jschProps);
// if (sftpPassword != null) {
factory.setPassword(sftpProperty.getPassword());
// } else {
// factory.setPrivateKey(sftpPrivateKey);
// factory.setPrivateKeyPassphrase(sftpPrivateKeyPassphrase);
// }
factory.setAllowUnknownKeys(true);
// //设置缓存的属性,缓存的size(), waitTimeout().
CachingSessionFactory<LsEntry> cachingSessionFactory =
new CachingSessionFactory<LsEntry>(factory);
cachingSessionFactory.setPoolSize(10);
// cachingSessionFactory.setSessionWaitTimeout(1000);
return cachingSessionFactory;
// return new CachingSessionFactory<LsEntry>(factory);
}*/
/**
* 创建 spring-integration-sftp session
* 避免使用jsch原生的创建session的方式
*
* @return SessionFactory<LsEntry>
*/
@Bean
public SessionFactory<LsEntry> sftpSessionFactory(){
System.out.println("######################################################");
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
factory.setUser(sftpProperty.getUser());
factory.setHost(sftpProperty.getHost());
factory.setPort(sftpProperty.getPort());
factory.setPassword(sftpProperty.getPassword());
Properties jschProps = new Properties();
//!important 必须配置PreferredAuthentications,否则程序控制台会询问user name 和 password。
jschProps.put("StrictHostKeyChecking", "no");
jschProps.put("PreferredAuthentications",
"password,gssapi-with-mic,publickey,keyboard-interactive");
factory.setSessionConfig(jschProps);
factory.setAllowUnknownKeys(true);
//设置缓存的属性,缓存的size(), waitTimeout().
CachingSessionFactory<LsEntry> cachingSessionFactory =
new CachingSessionFactory<LsEntry>(factory);
// cachingSessionFactory.setPoolSize(2000);
return cachingSessionFactory;
}
/**
* 配置Outbound Channel Adapter.
*
* 实质上就是一个MessageHandler,接收Message Channel发送的信息流.
* @return MessageHandler
*/
@ServiceActivator(inputChannel = "fileInChannel")
@Bean
public SftpMessageHandler sftpMessageHandler(){
SftpMessageHandler sftpMsgHandler = new SftpMessageHandler(sftpSessionFactory());
sftpMsgHandler.setRemoteDirectoryExpression(
new LiteralExpression(sftpProperty.getSftpAchievePath()));
sftpMsgHandler.setAutoCreateDirectory(true);
sftpMsgHandler.setCharset("UFT-8");
return sftpMsgHandler;
}
/**
* 配置 Inbound Channel Adapter
*
* 监控sftp服务器文件的状态。一旦由符合条件的文件生成,就将其同步到本地服务器。
* 需要条件:inboundFileChannel的bean;轮询的机制;文件同步bean,SftpInboundFileSynchronizer;
*/
@Bean
@InboundChannelAdapter(value = "inboundFileChannel",
poller = @Poller(cron = "0 1/10 * * * *", maxMessagesPerPoll = "1"))
public MessageSource<File> fileMessageSource() {
System.out.println("=========================================================");
//创建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;
}
///////////////////////////////////////////////////////////////////////
/**
* 配置 SFTP Outbound Gateway
*
* @return MessageHandler
*/
@Bean
@ServiceActivator(inputChannel = "sftpChannel")
public MessageHandler handler() {
SftpOutboundGateway sftpOutboundGateway = new SftpOutboundGateway(sftpSessionFactory(),"ls","payload");
// MessageChannel message = sftpOutboundGateway.getOutputChannel();
sftpOutboundGateway.setLocalDirectory(new File("E:\\sftp_tmp_dir"));
sftpOutboundGateway.setAutoCreateLocalDirectory(true); // TODO dynanic path
return sftpOutboundGateway;
}
@Bean
@ServiceActivator(inputChannel = "sftpChannel2")
public MessageHandler handler2() {
SftpOutboundGateway sftpOutboundGateway = new SftpOutboundGateway(sftpSessionFactory(),"ls","payload");
sftpOutboundGateway.setOptions("-dirs");
sftpOutboundGateway.setLocalDirectory(new File("E:\\sftp_tmp_dir"));
sftpOutboundGateway.setAutoCreateLocalDirectory(true); // TODO dynanic path
return sftpOutboundGateway;
}
@Bean
@ServiceActivator(inputChannel = "sftpChannel3")
public MessageHandler handler3() {
System.out.println("========================= 3 ================================");
SftpOutboundGateway sftpOutboundGateway = new SftpOutboundGateway(sftpSessionFactory(),"mget","payload");
sftpOutboundGateway.setOptions("-R");
sftpOutboundGateway.setFileExistsMode(FileExistsMode.REPLACE_IF_MODIFIED);
sftpOutboundGateway.setLocalDirectory(new File("E:\\sftp_tmp_dir"));
sftpOutboundGateway.setAutoCreateLocalDirectory(true); // TODO dynanic path
return sftpOutboundGateway;
}
@Autowired
private BeanFactory beanFactory;
//outbound gateway,put命令需要借助与sftpRemoteFileTemplate。
//看源码,可以发现outbound gateway 有多种构造函数;
@Bean
@ServiceActivator(inputChannel = "sftpChannel4")
public MessageHandler handler4(){
SftpRemoteFileTemplate sftpRemoteFileTemplate = new SftpRemoteFileTemplate(sftpSessionFactory());
sftpRemoteFileTemplate.setRemoteDirectoryExpression(new LiteralExpression("/send"));
SftpOutboundGateway sftpOutboundGateway = new SftpOutboundGateway(sftpRemoteFileTemplate,"put","payload");
// sftpOutboundGateway.setLocalDirectoryExpressionString("/get/");
sftpOutboundGateway.setBeanFactory(beanFactory);
return sftpOutboundGateway;
}
@Bean
@ServiceActivator(inputChannel = "sftpChannel5")
public MessageHandler handler5(){
SftpRemoteFileTemplate sftpRemoteFileTemplate = new SftpRemoteFileTemplate(sftpSessionFactory());
sftpRemoteFileTemplate.setRemoteDirectoryExpression(new LiteralExpression("/send"));
SftpOutboundGateway sftpOutboundGateway = new SftpOutboundGateway(sftpRemoteFileTemplate,"mput","payload");
// sftpOutboundGateway.setLocalDirectoryExpressionString("/get/");
// sftpOutboundGateway.setOptions("-R");
sftpOutboundGateway.setMputFilter(new FileListFilter<File>() {
@Override
public List<File> filterFiles(File[] files) {
return null;
}
});
sftpOutboundGateway.setBeanFactory(beanFactory);
return sftpOutboundGateway;
}
@Bean
@ServiceActivator(inputChannel = "sftpChannel6")
public MessageHandler handler6(){
SftpOutboundGateway sftpOutboundGateway = new SftpOutboundGateway(sftpSessionFactory(),"rm","payload");
sftpOutboundGateway.setBeanFactory(beanFactory);
return sftpOutboundGateway;
}
@Bean
@ServiceActivator(inputChannel = "sftpChannel7")
public MessageHandler handler7(){
// SftpRemoteFileTemplate sftpRemoteFileTemplate = new SftpRemoteFileTemplate(sftpSessionFactory());
// sftpRemoteFileTemplate.setRemoteDirectoryExpression(new LiteralExpression("/send"));
SftpOutboundGateway sftpOutboundGateway = new SftpOutboundGateway(sftpSessionFactory(),"mv","'send/22.TXT'");
// sftpOutboundGateway.setRenameExpression(new LiteralExpression("/send1"));
// sftpOutboundGateway.setChmod(777);
// sftpOutboundGateway.setRenameExpressionString("send1");
sftpOutboundGateway.setRenameExpression(new LiteralExpression("send1/22.TXT"));
// sftpOutboundGateway.setAutoCreateLocalDirectory(true);
sftpOutboundGateway.setBeanFactory(beanFactory);
return sftpOutboundGateway;
}
@MessagingGateway
public interface UploadGateway {
@Gateway(requestChannel = "sftpChannel")
List<FileInfo> listFileInfo(String dir);
@Gateway(requestChannel = "sftpChannel2")
List<FileInfo> listFileName(String dir);
@Gateway(requestChannel = "sftpChannel3")
List<File> listFile(String dir);
@Gateway(requestChannel = "sftpChannel4")
String putFile(File source);
@Gateway(requestChannel = "sftpChannel5")
List<String> mputFile(File directory);
@Gateway(requestChannel = "sftpChannel6")
boolean removeFile(String file);
@Gateway(requestChannel = "sftpChannel7")
boolean moveFile(String file);
}
}
映射yml文件里的stfp配置实体SftpProperty .java
package com.flower.integration.sftp;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component("sftpProperty")
@ConfigurationProperties(prefix = "sftp")
public class SftpProperty {
private String host;
private Integer port;
private String user;
private String password;
private Map<String,String> filePath;
////////////////////////////////////////////////////
public String getSftpSendPath(){
return filePath.get("send");
}
public String getSftpAchievePath(){
return filePath.get("achieve");
}
public String getLocalTempDir(){
return filePath.get("localPath");
}
///////////////////////////////////////////////////
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Map<String, String> getFilePath() {
return filePath;
}
public void setFilePath(Map<String, String> filePath) {
this.filePath = filePath;
}
}
Service层:SftpService.java
package com.flower.integration.sftp;
import com.jcraft.jsch.ChannelSftp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.file.support.FileExistsMode;
import org.springframework.integration.sftp.inbound.SftpInboundFileSynchronizer;
import org.springframework.integration.sftp.session.SftpRemoteFileTemplate;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
@Service("sftpService")
public class SftpService {
private Logger log = LoggerFactory.getLogger(this.getClass());
@Resource(name = "fileInChannel")
protected MessageChannel messageChannel;
@Autowired
private SftpProperty sftpProperty;
@Autowired
private SessionFactory<ChannelSftp.LsEntry> sftpSessionFactory;
/**
* 发送文件到SFTP, 借用MessageChannel
*
* @param localFilePath file local path.
*/
public void sendFileToSftp(String localFilePath) {
Path filePath = Paths.get(localFilePath);
if (filePath.toFile().exists()) {
Message<File> fileMessage = MessageBuilder.withPayload(filePath.toFile()).build();
boolean result = messageChannel.send(fileMessage);
String resultMsg = result ? "Success" : "Failure";
log.info("File send to sftp {}, File: {}.", resultMsg, filePath.getFileName());
} else {
log.warn("No found file. {}", filePath.getFileName());
}
}
/**
* 删除sftp文件
*
* @param sessionFactory sftp server.
* @param remoteDirectory file directory.
* @param fileName file
* @return return true is remove success,or false.
*/
public boolean removeSftpRemoteFile(SessionFactory<ChannelSftp.LsEntry> sessionFactory, String remoteDirectory, String fileName) {
SftpRemoteFileTemplate sftpRemoteFileTemplate = new SftpRemoteFileTemplate(sessionFactory);
boolean direCheck = remoteDirectory.endsWith(sftpRemoteFileTemplate.getRemoteFileSeparator());
if (!direCheck) {
remoteDirectory += sftpRemoteFileTemplate.getRemoteFileSeparator();
}
boolean fileExist = sftpRemoteFileTemplate.exists(remoteDirectory + fileName);
if (fileExist) {
return sftpRemoteFileTemplate.remove(remoteDirectory + fileName);
} else {
log.warn("No found file in the directory, {}.", remoteDirectory);
return false;
}
}
/**
* sftp文件重命名
*
* @param sessionFactory sftp server
* @param remoteDirectory file directory path.
* @param sourceFileName source file name
* @param targetFileName rename target name
*/
public void renameSftpRemoteFile(SessionFactory<ChannelSftp.LsEntry> sessionFactory, String remoteDirectory,
String sourceFileName, String targetFileName) {
SftpRemoteFileTemplate fileTemplate = new SftpRemoteFileTemplate(sessionFactory);
boolean direCheck = remoteDirectory.endsWith(fileTemplate.getRemoteFileSeparator());
if (!direCheck) {
remoteDirectory += fileTemplate.getRemoteFileSeparator();
}
boolean fileExist = fileTemplate.exists(remoteDirectory + sourceFileName);
if (fileExist) {
fileTemplate.rename(remoteDirectory + sourceFileName, remoteDirectory + targetFileName);
} else {
log.warn("No found file in the directory, {}.", remoteDirectory);
}
}
/**
* sftp文件是否存在
*
* @param sessionFactory sftp server
* @param directory file directory
* @param fileName file name
* @return true if file exist, or false.
*/
public boolean fileExist(SessionFactory<ChannelSftp.LsEntry> sessionFactory, String directory, String fileName) {
SftpRemoteFileTemplate fileTemplate = new SftpRemoteFileTemplate(sessionFactory);
boolean fileNameCheck = directory.endsWith(fileTemplate.getRemoteFileSeparator());
if (!fileNameCheck) {
directory += fileTemplate.getRemoteFileSeparator();
}
return fileTemplate.exists(directory + fileName);
}
/**
* sftp检索文件
*
* @param sessionFactory sftp server
* @param directory file directory
* @param fileNameFilter file name filter
* @return file name list match filter
*/
public List<String> lsFileOfDirectory(SessionFactory<ChannelSftp.LsEntry> sessionFactory,
String directory, String fileNameFilter) {
SftpRemoteFileTemplate fileTemplate = new SftpRemoteFileTemplate(sessionFactory);
if (!directory.endsWith(fileTemplate.getRemoteFileSeparator())) {
directory += fileTemplate.getRemoteFileSeparator();
}
ChannelSftp.LsEntry[] files = fileTemplate.list(directory + fileNameFilter);
List<String> fileNames = new ArrayList<>();
for (ChannelSftp.LsEntry lsEntry : files) {
boolean isDir = lsEntry.getAttrs().isDir();
if (!isDir) {
fileNames.add(lsEntry.getFilename());
}
}
return fileNames;
}
@Autowired
private BeanFactory beanFactory;
/**
* 本地发送文件至sftp服务器
*
* @param sessionFactory sftp server
* @param filePath file local path
* @param targetPath target directory
* @param mode FileExistsModel
* NULL:默认,替换文件;
* APPEND:若文件存在,追加内容;
* REPLACE:替换文件;
* APPEND_NO_FLUSH:
* FAIL:
* IGNORE:
*/
public void sendSftpFile(SessionFactory<ChannelSftp.LsEntry> sessionFactory,
String filePath, String targetPath, FileExistsMode mode){
SftpRemoteFileTemplate fileTemplate = new SftpRemoteFileTemplate(sessionFactory);
try {
//设置远程sftp服务器配置
fileTemplate.setRemoteDirectoryExpression(new LiteralExpression(targetPath));
fileTemplate.setAutoCreateDirectory(true);
fileTemplate.setCharset("UTF-8");
fileTemplate.setBeanFactory(beanFactory);
fileTemplate.afterPropertiesSet();
} catch (Exception e){
log.warn(e.getMessage());
}
Path file = Paths.get(filePath);
if (file.toFile().exists()){
Message<File> message = MessageBuilder.withPayload(file.toFile()).build();
if (null == mode){
fileTemplate.send(message);
} else {
//fileTemplate.setFileNameGenerator(new DefaultFileNameGenerator());
if (fileTemplate.isUseTemporaryFileName()){
fileTemplate.setUseTemporaryFileName(false);
}
fileTemplate.send(message, mode);
}
}
}
@Resource(name = "synFileChannel")
private SftpInboundFileSynchronizer sftpInboundFileSynchronizer;
public void synchronizedFileToLocal(String localDir){
File dir = Paths.get(localDir).toFile();
sftpInboundFileSynchronizer.synchronizeToLocalDirectory(dir);
}
}
Controller层:用于测试service层方法
package com.flower.integration.sftp;
import com.jcraft.jsch.ChannelSftp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.file.remote.FileInfo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.File;
import java.util.List;
@RestController
public class TestController {
@Autowired
private SftpService sftpService;
@Autowired
private SftpConfig.UploadGateway uploadGateway;
@GetMapping("/sftp")
public void testSftpSpringBatch() {
List<FileInfo> fileList = uploadGateway.listFileInfo("/send");
for (FileInfo file : fileList) {
String fileName = file.getFilename();
String filePath = file.getRemoteDirectory();
ChannelSftp.LsEntry fileInfo = (ChannelSftp.LsEntry) file.getFileInfo();
boolean isDir = file.isDirectory();
boolean isLink = file.isLink();
long modifyTime = file.getModified();
System.out.println("============================= " + fileName);
System.out.println("================== " + filePath);
System.out.println("================== " + fileInfo.getFilename());
System.out.println("================== " + isDir);
System.out.println("================== " + isLink);
System.out.println("================== " + modifyTime);
}
}
@GetMapping("/sftp2")
public void testSftpSpringBatch2() {
List<FileInfo> fileNameList = uploadGateway.listFileName("/send");
for (FileInfo fileName : fileNameList) {
System.out.println("============================= " + fileName);
}
}
@GetMapping("/sftp3")
public void testSftpSpringBatch3() throws InterruptedException {
List<File> fileNameList = uploadGateway.listFile("/send");
for (File fileName : fileNameList) {
System.out.println("============================= " + fileName);
}
}
@GetMapping("/sftp4")
public void testSftpSpringBatch4() throws InterruptedException {
String result = uploadGateway.putFile(new File("G:\\Redis.pdf"));
System.out.println("============================= " + result);
}
@GetMapping("/sftp5")
public void testSftpSpringBatch5() throws InterruptedException {
List<String> result = uploadGateway.mputFile(new File("G:\\js"));
for (String fileName : result) {
System.out.println("============================= " + fileName);
}
}
@GetMapping("/sftp6")
public void testSftpSpringBatch6() throws InterruptedException {
boolean result = uploadGateway.removeFile("/send/2.txt");
System.out.println("============================= " + result);
}
@GetMapping("/sftp7")
public void testSftpSpringBatch7() throws InterruptedException {
boolean result = uploadGateway.moveFile("/22.TXT");
System.out.println("============================= " + result);
}
}
SpringIntegrationApp.java启动类
package com.flower.integration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
//@EnableScheduling
public class SpringIntegrationApp {
public static void main(String[] args) {
SpringApplication.run(SpringIntegrationApp.class, args);
System.out.println("Spring-Integration application start success.");
}
}
junit单元测试类:自行在test文件夹下建立并测试SpringIntegrationExamplesApplicationTests.java
package com.flower.integration.sftp;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringIntegrationExamplesApplicationTests {
@Test
public void contextLoads() {
}
}
进行单元测试SftpServiceTest.java
package com.flower.integration.sftp;
import com.flower.integration.SpringIntegrationApp;
import com.jcraft.jsch.ChannelSftp;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.file.support.FileExistsMode;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringIntegrationApp.class)
@EnableIntegration
public class SftpServiceTest {
@Autowired
private SftpService sftpService;
@Autowired
private SftpProperty sftpProperty;
@Autowired
private SessionFactory<ChannelSftp.LsEntry> sftpSessionFactory;
@Before
public void before (){
System.out.println("00000000000000000000000000000000000000000000000000000");
}
@After
public void after(){
}
@Test
public void sendFileToSftp() {
//sftpService.sendFileToSftp();
}
@Test
public void testRemoveSftpRemoteFile(){
boolean result = sftpService.removeSftpRemoteFile(
sftpSessionFactory, sftpProperty.getSftpSendPath(),"user333.csv");
System.out.println("=======" + result);
}
@Test
public void testRenameSftpRemoteFile(){
sftpService.renameSftpRemoteFile(sftpSessionFactory, sftpProperty.getSftpSendPath(),"user.csv",
"user111.csv");
}
@Test
public void testfileExist(){
boolean result = sftpService.fileExist(sftpSessionFactory, sftpProperty.getSftpSendPath(),"user111.csv");
System.out.println("++++++++++++" + result);
}
@Test
public void testlsFileOfDirectory(){
List<String> result = sftpService.lsFileOfDirectory(sftpSessionFactory,
sftpProperty.getSftpSendPath(),"*TXT");
System.out.println("-------------------" + result.toString());
}
@Test
public void testSendSftpFile() throws Exception {
sftpService.sendSftpFile(sftpSessionFactory,
"G:\\jquery.txt", sftpProperty.getSftpAchievePath(), FileExistsMode.REPLACE);
}
@Test
public void testSynchronizedFileToLocal(){
sftpService.synchronizedFileToLocal(sftpProperty.getLocalTempDir());
}
}