SFTP 出站通道适配器
SFTP出站通道适配器是一种特殊的MessageHandler,它连接到远程目录,并为接收到的每个作为入站Message有效负载的文件发起文件传输。它还支持文件的多种表示形式,因此您不仅限于File对象。与FTP出站适配器类似,SFTP出站通道适配器支持以下有效负载:
-
java.io.File: 实际的文件对象 -
byte[]: 表示文件内容的字节数组 -
java.lang.String: 表示文件内容的文本 -
java.io.InputStream: 用于传输到远程文件的数据流 -
org.springframework.core.io.Resource: 用于传输到远程文件的数据资源
以下示例展示了如何配置一个SFTP出站通道适配器:
<int-sftp:outbound-channel-adapter id="sftpOutboundAdapter"
session-factory="sftpSessionFactory"
channel="inputChannel"
charset="UTF-8"
remote-file-separator="/"
remote-directory="foo/bar"
remote-filename-generator-expression="payload.getName() + '-mysuffix'"
filename-generator="fileNameGenerator"
use-temporary-filename="true"
chmod="600"
mode="REPLACE"/>
有关这些属性的更多详细信息,请参阅 schema。
SpEL 与 SFTP 出站适配器
与 Spring Integration 中的许多其他组件一样,在配置 SFTP 出站通道适配器时,您可以通过指定两个属性来使用 Spring 表达式语言(SpEL):remote-directory-expression 和 remote-filename-generator-expression(前文已介绍)。表达式求值上下文以消息作为其根对象,这使您可以使用表达式根据消息中的数据(来自 'payload' 或 'headers')动态计算文件名或现有目录路径。在前面的示例中,我们定义了 remote-filename-generator-expression 属性,其表达式值基于原始文件名计算文件名,同时附加后缀:'-mysuffix'。
从 4.1 版本开始,您可以在传输文件时指定 mode。默认情况下,现有文件会被覆盖。模式由 FileExistsMode 枚举定义,包含以下值:
-
REPLACE(默认) -
REPLACE_IF_MODIFIED -
APPEND -
APPEND_NO_FLUSH -
IGNORE -
FAIL
使用 IGNORE 和 FAIL 时,文件不会被传输。FAIL 会引发异常,而 IGNORE 则会静默忽略传输(尽管会生成一条 DEBUG 日志记录)。
在 4.3 版本中引入了 chmod 属性,您可以在上传后使用它来更改远程文件的权限。您可以使用传统的 Unix 八进制格式(例如,600 表示仅允许文件所有者读写)。当使用 Java 配置适配器时,您可以使用 setChmodOctal("600") 或 setChmod(0600)。
避免部分写入的文件
处理文件传输时的一个常见问题是可能处理到不完整的文件。文件可能在传输尚未完成时就出现在文件系统中。
为了解决这一问题,Spring Integration SFTP适配器采用了一种通用算法:文件在传输过程中使用临时名称,待完全传输后再进行重命名。
默认情况下,正在传输的每个文件在文件系统中都会显示为带有额外后缀,默认后缀为 .writing。您可以通过设置 temporary-file-suffix 属性来更改此后缀。
然而,在某些情况下,您可能不希望使用此技术(例如,当服务器不允许重命名文件时)。针对此类情况,您可以通过将 use-temporary-file-name 设置为 false(默认为 true)来禁用此功能。当此属性为 false 时,文件将以最终名称写入,消费应用程序需要其他机制来检测文件已完全上传,然后才能访问它。
使用 Java 配置进行配置
以下Spring Boot应用程序展示了如何使用Java配置出站适配器的示例:
@SpringBootApplication
@IntegrationComponentScan
public class SftpJavaApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context =
new SpringApplicationBuilder(SftpJavaApplication.class)
.web(false)
.run(args);
MyGateway gateway = context.getBean(MyGateway.class);
gateway.sendToSftp(new File("/foo/bar.txt"));
}
@Bean
public SessionFactory<SftpClient.DirEntry> 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<SftpClient.DirEntry>(factory);
}
@Bean
@ServiceActivator(inputChannel = "toSftpChannel")
public MessageHandler handler() {
SftpMessageHandler handler = new SftpMessageHandler(sftpSessionFactory());
handler.setRemoteDirectoryExpressionString("headers['remote-target-dir']");
handler.setFileNameGenerator(new FileNameGenerator() {
@Override
public String generateFileName(Message<?> message) {
return "handlerContent.test";
}
});
return handler;
}
@MessagingGateway
public interface MyGateway {
@Gateway(requestChannel = "toSftpChannel")
void sendToSftp(File file);
}
}
使用 Java DSL 进行配置
以下Spring Boot应用程序展示了如何使用Java DSL配置出站适配器的示例:
@SpringBootApplication
public class SftpJavaApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(SftpJavaApplication.class)
.web(false)
.run(args);
}
@Bean
public IntegrationFlow sftpOutboundFlow() {
return IntegrationFlow.from("toSftpChannel")
.handle(Sftp.outboundAdapter(this.sftpSessionFactory, FileExistsMode.FAIL)
.useTemporaryFileName(false)
.remoteDirectory("/foo")
).get();
}
}