SFTP 送信チャネルアダプター
SFTP 発信チャネルアダプターは、リモートディレクトリに接続し、受信 Message のペイロードとして受信するすべてのファイルに対してファイル転送を開始する特別な MessageHandler です。また、ファイルのいくつかの表現もサポートしているため、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"/>これらの属性の詳細については、スキーマ [GitHub] (英語) を参照してください。
SpEL と SFTP 送信アダプター
Spring Integration の他の多くのコンポーネントと同様に、SFTP 送信チャネルアダプターを構成するときに、remote-directory-expression と remote-filename-generator-expression ( 前述 ) の 2 つの属性を指定することで、Spring 式言語 (SpEL) を使用できます。式評価コンテキストにはルートオブジェクトとしてメッセージがあり、メッセージ内のデータ (「ペイロード」または「ヘッダー」から) に基づいてファイル名または既存のディレクトリパスを動的に計算できる式を使用できます。前の例では、元の名前に基づいてファイル名を計算し、サフィックス "-mysuffix" も追加する式の値を使用して remote-filename-generator-expression 属性を定義します。
バージョン 4.1 以降、ファイルを転送するときに mode を指定できます。デフォルトでは、既存のファイルは上書きされます。モードは、次の値を含む FileExistsMode 列挙によって定義されます。
REPLACE(default)REPLACE_IF_MODIFIEDAPPENDAPPEND_NO_FLUSHIGNOREFAIL
IGNORE および FAIL では、ファイルは転送されません。FAIL は例外をスローしますが、IGNORE は通知なしで転送を無視します(DEBUG ログエントリは生成されます)。
バージョン 4.3 では、chmod 属性が導入されました。これを使用すると、アップロード後にリモートファイルのパーミッションを変更できます。従来の Unix 8 進形式を使用できます(たとえば、600 はファイル所有者のみに読み取り / 書き込みを許可します)。java を使用してアダプターを設定する場合は、setChmodOctal("600") または setChmod(0600) を使用できます。
部分的に書き込まれたファイルの回避
ファイル転送を処理する際の一般的な問題の 1 つは、部分的なファイルを処理する可能性です。転送が実際に完了する前に、ファイルがファイルシステムに表示される場合があります。
この課題に対処するために、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();
}
}