SFTP 送信ゲートウェイ

SFTP 送信ゲートウェイは、リモート SFTP サーバーとやり取りできる限られたコマンドセットを提供します。

  • ls (リストファイル)

  • nlst (リストファイル名)

  • get (ファイルを取得する)

  • mget (複数のファイルを取得する)

  • rm (ファイルを削除)

  • mv (ファイルの移動と名前変更)

  • put (ファイルを送る)

  • mput (複数のファイルを送信する)

ls コマンドの使用

ls はリモートファイルをリストし、次のオプションをサポートします。

  • -1: ファイル名のリストを取得します。デフォルトでは、FileInfo オブジェクトのリストを取得します

  • -a: すべてのファイルを含める ( "." で始まるものを含む)

  • -f: リストをソートしないでください

  • -dirs: インクルードディレクトリ (デフォルトで除外)

  • -links: シンボリックリンクを含める (デフォルトで除外)

  • -R: リモートディレクトリを再帰的にリストする

さらに、ファイル名のフィルタリングは inbound-channel-adapter と同じ方法で提供されます。

ls 操作から生じるメッセージペイロードは、ファイル名のリストまたは FileInfo オブジェクトのリストです(-1 スイッチを usr するかどうかによって異なります)。これらのオブジェクトは、変更された時間、権限などの情報を提供します。

ls コマンドが実行されたリモートディレクトリは、file_remoteDirectory ヘッダーで提供されます。

再帰オプション(-R)を使用する場合、fileName にはサブディレクトリ要素が含まれ、ファイルへの相対パス(リモートディレクトリに対する相対パス)を表します。-dirs オプションを使用する場合、各再帰ディレクトリもリスト内の要素として返されます。この場合、-1 オプションを使用しないことをお勧めします。FileInfo オブジェクトを使用するとファイルとディレクトリを区別できなくなるためです。

リストする リモートのパスが / 記号で始まる場合、SFTP では現在のユーザーホームの相対パスとしてではなく、絶対パスとして扱われます。

nlst コマンドの使用

バージョン 5 では、nlst コマンドのサポートが導入されました。

nlst はリモートファイル名をリストし、1 つのオプションのみをサポートします。

  • -f: リストをソートしないでください

nlst 操作から生じるメッセージペイロードは、ファイル名のリストです。

file_remoteDirectory ヘッダーには、nlst コマンドが実行されたリモートディレクトリが保持されます。

SFTP プロトコルは、名前をリストする機能を提供しません。このコマンドは、-1 オプションを指定した ls コマンドに相当し、便宜上ここに追加されます。

get コマンドの使用

get はリモートファイルを取得し、次のオプションをサポートします。

  • -P: リモートファイルのタイムスタンプを保持します。

  • -stream: リモートファイルをストリームとして取得します。

  • -D: 転送に成功したら、リモートファイルを削除します。FileExistsMode は IGNORE であり、ローカルファイルがすでに存在するため、転送が無視される場合、リモートファイルは削除されません。

file_remoteDirectory ヘッダーはリモートディレクトリを保持し、file_remoteFile ヘッダーはファイル名を保持します。

get 操作の結果として得られるメッセージペイロードは、取得されたファイルを表す File オブジェクトです。-stream オプションを使用する場合、ペイロードは File ではなく InputStream になります。テキストファイルの場合、一般的な使用例は、この操作をファイルスプリッターまたはストリームトランスフォーマーと組み合わせることです。リモートファイルをストリームとして使用する場合、ストリームの使用後に Session を閉じる必要があります。便宜上、Session は closeableResource ヘッダーで提供され、IntegrationMessageHeaderAccessor は便利なメソッドを提供します。

Closeable closeable = new IntegrationMessageHeaderAccessor(message).getCloseableResource();
if (closeable != null) {
    closeable.close();
}

ファイル分割ストリームトランスなどのフレームワークコンポーネントは、データの転送後にセッションを自動的に閉じます。

次の例は、ファイルをストリームとして使用する方法を示しています。

<int-sftp:outbound-gateway session-factory="ftpSessionFactory"
                            request-channel="inboundGetStream"
                            command="get"
                            command-options="-stream"
                            expression="payload"
                            remote-directory="ftpTarget"
                            reply-channel="stream" />

<int-file:splitter input-channel="stream" output-channel="lines" />
カスタムコンポーネントで入力ストリームを使用する場合は、Session を閉じる必要があります。次の例に示すように、カスタムコードでそれを行うか、メッセージのコピーを service-activator にルーティングして SpEL を使用できます。
<int:service-activator input-channel="closeSession"
    expression="headers['closeableResource'].close()" />

mget コマンドの使用

mget は、パターンに基づいて複数のリモートファイルを取得し、次のオプションをサポートします。

  • -P: リモートファイルのタイムスタンプを保持します。

  • -R: ディレクトリツリー全体を再帰的に取得します。

  • -x: パターンに一致するファイルがない場合は例外をスローします(そうでない場合は空のリストが返されます)。

  • -D: 転送が成功したら、各リモートファイルを削除します。転送が無視された場合、FileExistsMode は IGNORE であり、ローカルファイルがすでに存在するため、リモートファイルは削除されません。

mget 操作の結果のメッセージペイロードは、List<File> オブジェクト(つまり、File オブジェクトの List、それぞれが取得したファイルを表す)です。

バージョン 5.0 以降、FileExistsMode が IGNORE の場合、出力メッセージのペイロードには、ファイルがすでに存在するためにフェッチされなかったファイルが含まれなくなりました。以前は、配列には既存のファイルを含むすべてのファイルが含まれていました。

使用する式は、リモートパスが * で終わる結果を生成する必要があることを決定します。たとえば、myfiles/* は myfiles の完全なツリーをフェッチします。

バージョン 5.0 以降、FileExistsMode.REPLACE_IF_MODIFIED モードと組み合わせて再帰的な MGET を使用して、リモートディレクトリツリー全体を定期的にローカルに同期できます。このモードは、-P (タイムスタンプを保持)オプションに関係なく、ローカルファイルの最終変更タイムスタンプをリモートファイルのタイムスタンプに設定します。

再帰を使用する場合の注意 (-R)

パターンは無視され、* が想定されます。デフォルトでは、リモートツリー全体が取得されます。ただし、FileListFilter を提供することにより、ツリー内のファイルをフィルタリングできます。この方法でツリー内のディレクトリをフィルタリングすることもできます。FileListFilter は、参照によって、または filename-pattern または filename-regex 属性によって提供されます。例: filename-regex="(subDir|.*1.txt)" は、リモートディレクトリおよびサブディレクトリ subDir 内の 1.txt で終わるすべてのファイルを取得します。ただし、この注の後に利用可能な代替手段について説明します。

サブディレクトリをフィルタリングすると、そのサブディレクトリの追加の走査は実行されません。

-dirs オプションは許可されていません(再帰的な mget は再帰的な ls を使用してディレクトリツリーを取得し、ディレクトリ自体をリストに含めることはできません)。

通常、リモートディレクトリ構造がローカルに保持されるように、local-directory-expression で #remoteDirectory 変数を使用します。

永続ファイルリストフィルターにブールプロパティ forRecursion が追加されました。このプロパティを true に設定すると、alwaysAcceptDirectories も設定されます。これは、送信ゲートウェイ(ls および mget)での再帰操作が常にディレクトリツリー全体を毎回トラバースすることを意味します。これは、ディレクトリツリーの奥深くで変更が検出されなかった問題を解決するためです。さらに、forRecursion=true により、ファイルへのフルパスがメタデータストアキーとして使用されます。これにより、同じ名前のファイルが異なるディレクトリに複数回表示された場合にフィルターが正しく機能しなかった問題が解決されます。重要: これは、永続メタデータストア内の既存のキーが、最上位ディレクトリにあるファイルで見つからないことを意味します。このため、プロパティはデフォルトで false です。これは将来のリリースで変更される可能性があります。

バージョン 5.0 から、alwaysAcceptDirectorties を true に設定することにより、常にディレクトリを渡すように SftpSimplePatternFileListFilter および SftpRegexPatternFileListFilter を構成できます。これにより、次の例に示すように、単純なパターンの再帰が可能になります。

<bean id="starDotTxtFilter"
            class="org.springframework.integration.sftp.filters.SftpSimplePatternFileListFilter">
    <constructor-arg value="*.txt" />
    <property name="alwaysAcceptDirectories" value="true" />
</bean>

<bean id="dotStarDotTxtFilter"
            class="org.springframework.integration.sftp.filters.SftpRegexPatternFileListFilter">
    <constructor-arg value="^.*\.txt$" />
    <property name="alwaysAcceptDirectories" value="true" />
</bean>

ゲートウェイで filter プロパティを使用して、これらのフィルターのいずれかを提供できます。

put コマンドの使用

put はファイルをリモートサーバーに送信します。メッセージのペイロードは、java.io.Filebyte[]String にすることができます。remote-filename-generator (または式)は、リモートファイルに名前を付けるために使用されます。その他の使用可能な属性には、remote-directorytemporary-remote-directory、それらに相当する *-expression (use-temporary-file-name および auto-create-directory)が含まれます。詳細については、スキーマのドキュメント [GitHub] (英語) を参照してください。

put 操作から生じるメッセージペイロードは、転送後のサーバー上のファイルの完全パスを含む String です。

バージョン 4.3 では chmod 属性が導入され、アップロード後にリモートファイルのアクセス許可が変更されます。従来の Unix 8 進形式を使用できます(たとえば、600 では、ファイル所有者に対してのみ読み取り / 書き込みが許可されます)。java を使用してアダプターを構成する場合、setChmod(0600) を使用できます。

mput コマンドの使用

mput は複数のファイルをサーバーに送信し、次のオプションをサポートします。

  • -R: 再帰 — ディレクトリとサブディレクトリ内のすべてのファイルを送信します(フィルタリングされている場合もあります)

メッセージペイロードは、ローカルディレクトリを表す java.io.File (または String)でなければなりません。バージョン 5.1 以降、File または String のコレクションもサポートされています。

put コマンドと同じ属性がサポートされています。さらに、mput-patternmput-regexmput-filter または mput-filter-expression のいずれかを使用して、ローカルディレクトリ内のファイルをフィルタリングできます。サブディレクトリ自体がフィルターを通過する限り、フィルターは再帰で機能します。フィルターを通過しないサブディレクトリは再帰されません。

mput 操作から生じるメッセージペイロードは、List<String> オブジェクト(つまり、転送から生じるリモートファイルパスの List)です。

バージョン 4.3 では、chmod 属性が導入されました。これにより、アップロード後にリモートファイルの権限を変更できます。従来の Unix 8 進形式を使用できます(たとえば、600 では、ファイル所有者に対してのみ読み取り / 書き込みが許可されます)。Java を使用してアダプターを構成する場合、setChmodOctal("600") または setChmod(0600) を使用できます。

rm コマンドの使用

rm コマンドにはオプションがありません。

削除操作が成功した場合、結果のメッセージペイロードは Boolean.TRUE です。それ以外の場合、メッセージペイロードは Boolean.FALSE です。file_remoteDirectory ヘッダーはリモートディレクトリを保持し、file_remoteFile ヘッダーはファイル名を保持します。

mv コマンドの使用

mv コマンドにはオプションがありません。

expression 属性は "from" パスを定義し、rename-expression 属性は "to" パスを定義します。デフォルトでは、rename-expression は headers['file_renameTo'] です。この式は、null または空の String と評価されてはなりません。必要に応じて、必要なリモートディレクトリが作成されます。結果メッセージのペイロードは Boolean.TRUE です。file_remoteDirectory ヘッダーは元のリモートディレクトリを保持し、file_remoteFile ヘッダーはファイル名を保持します。file_renameTo ヘッダーは新しいパスを保持します。

バージョン 5.5.6 以降、remoteDirectoryExpression を mv コマンドで使用できるようになりました。"from" ファイルが完全なファイルパスでない場合、remoteDirectoryExpression の結果がリモートディレクトリとして使用されます。同じことが "to" ファイルにも当てはまります。たとえば、タスクがディレクトリ内のリモートファイルの名前を変更するだけの場合です。

追加のコマンド情報

get および mget コマンドは、local-filename-generator-expression 属性をサポートしています。転送中にローカルファイルの名前を生成する SpEL 式を定義します。評価コンテキストのルートオブジェクトはリクエストメッセージです。remoteFileName 変数も使用できます。mget で特に役立ちます(例: local-filename-generator-expression="#remoteFileName.toUpperCase() + headers.foo")。

get および mget コマンドは、local-directory-expression 属性をサポートしています。転送中にローカルディレクトリの名前を生成する SpEL 式を定義します。評価コンテキストのルートオブジェクトはリクエストメッセージです。remoteDirectory 変数も使用できます。mget で特に役立ちます(例: local-directory-expression="'/tmp/local/' + #remoteDirectory.toUpperCase() + headers.myheader")。この属性は、local-directory 属性と相互に排他的です。

すべてのコマンドについて、ゲートウェイの「式」プロパティは、コマンドが動作するパスを保持します。mget コマンドの場合、式は * に評価される場合があります。これは、すべてのファイル somedirectory/*、および * で終わるその他の値を取得することを意味します。

次の例は、ls コマンド用に構成されたゲートウェイを示しています。

<int-ftp:outbound-gateway id="gateway1"
        session-factory="ftpSessionFactory"
        request-channel="inbound1"
        command="ls"
        command-options="-1"
        expression="payload"
        reply-channel="toSplitter"/>

toSplitter チャネルに送信されるメッセージのペイロードは、String オブジェクトのリストであり、各オブジェクトにはファイルの名前が含まれています。command-options="-1" を省略した場合、ペイロードは FileInfo オブジェクトのリストになります。オプションをスペース区切りリストとして提供できます(例: command-options="-1 -dirs -links")。

バージョン 4.2 以降、GETMGETPUTMPUT コマンドは FileExistsMode プロパティ(名前空間サポートを使用する場合は mode)をサポートします。これは、ローカルファイルが存在する場合(GET および MGET)またはリモートファイルが存在する場合(PUT および MPUT)の動作に影響します。サポートされているモードは REPLACEAPPENDFAILIGNORE です。下位互換性のために、PUT および MPUT 操作のデフォルトモードは REPLACE です。GET および MGET 操作の場合、デフォルトは FAIL です。

Java 構成を使用した構成

次の Spring Boot アプリケーションは、Java で送信ゲートウェイを構成する方法の例を示しています。

@SpringBootApplication
public class SftpJavaApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(SftpJavaApplication.class)
            .web(false)
            .run(args);
    }

    @Bean
    @ServiceActivator(inputChannel = "sftpChannel")
    public MessageHandler handler() {
        return new SftpOutboundGateway(ftpSessionFactory(), "ls", "'my_remote_dir/'");
    }

}

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 SessionFactory<SftpClient.DirEntry> sftpSessionFactory() {
        DefaultSftpSessionFactory sf = new DefaultSftpSessionFactory();
        sf.setHost("localhost");
        sf.setPort(port);
        sf.setUsername("foo");
        sf.setPassword("foo");
        factory.setTestSession(true);
        return new CachingSessionFactory<>(sf);
    }

    @Bean
    public QueueChannelSpec remoteFileOutputChannel() {
        return MessageChannels.queue();
    }

    @Bean
    public IntegrationFlow sftpMGetFlow() {
        return IntegrationFlow.from("sftpMgetInputChannel")
            .handle(Sftp.outboundGateway(sftpSessionFactory(),
                            AbstractRemoteFileOutboundGateway.Command.MGET, "payload")
                    .options(AbstractRemoteFileOutboundGateway.Option.RECURSIVE)
                    .regexFileNameFilter("(subSftpSource|.*1.txt)")
                    .localDirectoryExpression("'myDir/' + #remoteDirectory")
                    .localFilenameExpression("#remoteFileName.replaceFirst('sftpSource', 'localTarget')"))
            .channel("remoteFileOutputChannel")
            .get();
    }

}

送信ゲートウェイの部分的な成功 (mget および mput)

mget および mput を使用して)複数のファイルで操作を実行する場合、1 つ以上のファイルが転送された後、しばらく時間が経過すると例外が発生することがあります。この場合(バージョン 4.2 以降)、PartialSuccessException がスローされます。通常の MessagingException プロパティ(failedMessage および cause)と同様に、この例外には 2 つの追加プロパティがあります。

  • partialResults: 正常な転送結果。

  • derivedInput: リクエストメッセージから生成されたファイルのリスト(mput 用に転送するローカルファイルなど)。

これらの属性により、正常に転送されたファイルと転送されなかったファイルを判別できます。

再帰的な mput の場合、PartialSuccessException にはネストされた PartialSuccessException インスタンスがあります。

次のディレクトリ構造を考慮してください。

root/
|- file1.txt
|- subdir/
   | - file2.txt
   | - file3.txt
|- zoo.txt

file3.txt で例外が発生した場合、ゲートウェイによってスローされた PartialSuccessException には、file1.txtsubdirzoo.txt の derivedInput と file1.txt の partialResults があります。その cause は、file2.txt の derivedInput と file2.txt の file3.txt および partialResults を備えた別の PartialSuccessException です。