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_MODIFIED

  • APPEND

  • APPEND_NO_FLUSH

  • IGNORE

  • FAIL

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

}