跳到主要内容

SFTP 外发通道适配器

QWen Plus 中英对照 SFTP Outbound Channel Adapter

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"/>
xml

请参阅schema以了解更多关于这些属性的详细信息。

SpEL 和 SFTP 输出适配器

与 Spring Integration 中的许多其他组件一样,您可以在配置 SFTP 外发通道适配器时使用 Spring 表达式语言 (SpEL),方法是指定两个属性:remote-directory-expressionremote-filename-generator-expression (之前描述过)。表达式求值上下文以消息作为其根对象,这使您可以使用能够根据消息中的数据(来自 'payload' 或 'headers')动态计算文件名或现有目录路径的表达式。在前面的例子中,我们定义了 remote-filename-generator-expression 属性,其表达式值根据原始文件名计算文件名,同时追加一个后缀:'-mysuffix'。

从 4.1 版开始,您可以在传输文件时指定 mode。默认情况下,现有文件将被覆盖。模式由 FileExistsMode 枚举定义,包括以下值:

  • REPLACE (默认)

  • REPLACE_IF_MODIFIED

  • APPEND

  • APPEND_NO_FLUSH

  • IGNORE

  • FAIL

使用 IGNOREFAIL 时,文件不会被传输。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

使用 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();
}

}
java