ファイルを書く
ファイルシステムにメッセージを書き込むには、FileWritingMessageHandler (Javadoc) を使用できます。このクラスは、次のペイロード型を処理できます。
FileStringByte 配列
InputStream( バージョン 4.2 以降)
String ペイロードの場合、エンコードと文字セットを構成できます。
物事を簡単にするために、XML 名前空間を使用して、FileWritingMessageHandler を送信チャネルアダプターまたは送信ゲートウェイの一部として構成できます。
バージョン 4.3 以降、ファイルの書き込み時に使用するバッファーサイズを指定できます。
バージョン 5.1 以降では、FileExistsMode.APPEND または FileExistsMode.APPEND_NO_FLUSH を使用し、新しいファイルを作成する必要がある場合にトリガーされる BiConsumer<File, Message<?>> newFileCallback を提供できます。このコールバックは、新しく作成されたファイルとそれをトリガーしたメッセージを受け取ります。このコールバックは、たとえば、メッセージヘッダーで定義された CSV ヘッダーを書き込むために使用できます。
ファイル名の生成
最も単純な形式では、FileWritingMessageHandler はファイルを書き込むための宛先ディレクトリのみを必要とします。書き込まれるファイルの名前は、ハンドラーの FileNameGenerator (Javadoc) によって決定されます。デフォルトの実装 (Javadoc) は、FileHeaders.FILENAME (Javadoc) として定義された定数とキーが一致するメッセージヘッダーを探します。
あるいは、メッセージに対して評価される式を指定して、ファイル名を生成できます(例: headers['myCustomHeader'] + '.something')。式は String に評価される必要があります。便宜上、DefaultFileNameGenerator には setHeaderName メソッドも用意されており、ファイル名として使用される値を持つメッセージヘッダーを明示的に指定できます。
一度設定すると、DefaultFileNameGenerator は次の解決手順を使用して、特定のメッセージペイロードのファイル名を決定します。
メッセージに対して式を評価し、結果が空でない
Stringである場合、それをファイル名として使用します。それ以外の場合、ペイロードが
java.io.Fileの場合、Fileオブジェクトのファイル名を使用します。それ以外の場合は、メッセージ ID にを追加して使用します。ファイル名として
msg。
XML 名前空間のサポートを使用すると、ファイル送信チャネルアダプターとファイル送信ゲートウェイの両方が、以下の相互に排他的な構成属性をサポートします。
filename-generator(FileNameGenerator実装への参照)filename-generator-expression(Stringに評価される式)
ファイルの書き込み中に、一時ファイルのサフィックスが使用されます(デフォルトは .writing です)。ファイルの書き込み中にファイル名に追加されます。サフィックスをカスタマイズするには、ファイル送信チャネルアダプターとファイル送信ゲートウェイの両方で temporary-file-suffix 属性を設定できます。
APPEND ファイル mode を使用する場合、データはファイルに直接追加されるため、temporary-file-suffix 属性は無視されます。 |
バージョン 4.2.5 以降、生成されたファイル名(filename-generator または filename-generator-expression の評価の結果として)は、ターゲットファイル名と共に子パスを表すことができます。これは、以前と同様に File(File parent, String child) の 2 番目のコンストラクター引数として使用されます。ただし、以前はファイル名のみを想定し、子パス用のディレクトリ(mkdirs())は作成していませんでした。このアプローチは、ファイルシステムツリーをソースディレクトリと一致するように復元する必要がある場合に役立ちます。たとえば、アーカイブを解凍し、ターゲットディレクトリ内のすべてのファイルを元の順序で保存する場合などです。
出力ディレクトリの指定
ファイル送信チャネルアダプターとファイル送信ゲートウェイの両方に、出力ディレクトリを指定するための相互に排他的な 2 つの構成属性が用意されています。
directorydirectory-expression
Spring Integration 2.2 は directory-expression 属性を導入しました。 |
directory 属性の使用
directory 属性を使用すると、出力ディレクトリは固定値に設定されます。これは、FileWritingMessageHandler の初期化時に設定されます。この属性を指定しない場合は、directory-expression 属性を使用する必要があります。
directory-expression 属性の使用
SpEL を完全にサポートしたい場合は、directory-expression 属性を使用できます。この属性は、処理されるメッセージごとに評価される SpEL 式を受け入れます。出力ファイルディレクトリを動的に指定すると、メッセージのペイロードとそのヘッダーにフルアクセスできます。
SpEL 式は、String、java.io.File または org.springframework.core.io.Resource のいずれかに解決される必要があります。(後者は File に評価されます) さらに、結果の String または File はディレクトリを指している必要があります。directory-expression 属性を指定しない場合は、directory 属性を設定する必要があります。
auto-create-directory 属性の使用
デフォルトでは、宛先ディレクトリが存在しない場合、それぞれの宛先ディレクトリと存在しない親ディレクトリが自動的に作成されます。その動作を防ぐために、auto-create-directory 属性を false に設定できます。この属性は、directory 属性と directory-expression 属性の両方に適用されます。
アダプターの初期化時に宛先ディレクトリの存在を確認する代わりに、処理中の各メッセージに対してこの確認が実行されるようになりました。 さらに、 |
既存の宛先ファイルの処理
ファイルを書き込み、宛先ファイルがすでに存在する場合、デフォルトの動作ではそのターゲットファイルが上書きされます。関連するファイル送信コンポーネントの mode 属性を設定することにより、この動作を変更できます。次のオプションがあります。
REPLACE(デフォルト)REPLACE_IF_MODIFIEDAPPENDAPPEND_NO_FLUSHFAILIGNORE
Spring Integration 2.2 は、mode 属性と APPEND、FAIL、IGNORE オプションを導入しました。 |
REPLACEターゲットファイルがすでに存在する場合、上書きされます。
mode属性が指定されていない場合、これはファイルを書き込むときのデフォルトの動作です。REPLACE_IF_MODIFIEDターゲットファイルがすでに存在する場合、最後に変更されたタイムスタンプがソースファイルのタイムスタンプと異なる場合にのみ上書きされます。
Fileペイロードの場合、ペイロードlastModified時間は既存のファイルと比較されます。他のペイロードの場合、FileHeaders.SET_MODIFIED(file_setModified)ヘッダーが既存のファイルと比較されます。ヘッダーがないか、値がNumberでない場合、ファイルは常に置き換えられます。APPENDこのモードでは、毎回新しいファイルを作成する代わりに、既存のファイルにメッセージコンテンツを追加できます。この属性は
temporary-file-suffix属性と相互に排他的であることに注意してください。これは、既存のファイルにコンテンツを追加するときに、アダプターが一時ファイルを使用しなくなるためです。ファイルは各メッセージの後に閉じられます。APPEND_NO_FLUSHこのオプションのセマンティクスは
APPENDと同じですが、データはフラッシュされず、各メッセージの後にファイルは閉じられません。これにより、障害が発生した場合にデータ損失のリスクを伴う大きなパフォーマンスを提供できます。詳細については、APPEND_NO_FLUSH使用時のファイルのフラッシュを参照してください。FAILターゲットファイルが存在する場合、
MessageHandlingException(Javadoc) がスローされます。IGNOREターゲットファイルが存在する場合、メッセージペイロードは警告なしに無視されます。
一時ファイルのサフィックスを使用する場合(デフォルトは .writing)、最終ファイル名または一時ファイル名のいずれかが存在する場合、IGNORE オプションが適用されます。 |
APPEND_NO_FLUSH 使用時のファイルのフラッシュ
APPEND_NO_FLUSH モードは、バージョン 4.3 で追加されました。各メッセージの後にファイルが閉じられないため、これを使用するとパフォーマンスが向上します。ただし、これにより、障害発生時にデータが失われる可能性があります。
Spring Integration は、このデータ損失を軽減するためのいくつかのフラッシュ戦略を提供します。
flushIntervalを使用します。この期間ファイルが書き込まれない場合、自動的にフラッシュされます。これは概算であり、今回は1.33xまで可能です(平均1.167x)。正規表現を含むメッセージをメッセージハンドラーの
triggerメソッドに送信します。パターンに一致する絶対パス名を持つファイルはフラッシュされます。ハンドラーにカスタム
MessageFlushPredicate実装を提供して、メッセージがtriggerメソッドに送信されたときに実行されるアクションを変更します。カスタム
FileWritingMessageHandler.FlushPredicateまたはFileWritingMessageHandler.MessageFlushPredicate実装を渡すことにより、ハンドラーのflushIfNeededメソッドの 1 つを呼び出します。
述語は、開いているファイルごとに呼び出されます。詳細については、これらのインターフェースの Javadoc を参照してください。バージョン 5.0 以降、述語メソッドは別のパラメーターを提供することに注意してください。現在のファイルが新規または以前に閉じられた場合に最初に書き込まれた時間です。
flushInterval を使用する場合、間隔は最後の書き込みから始まります。ファイルは、その間隔でアイドル状態の場合にのみフラッシュされます。バージョン 4.3.7 から、追加のプロパティ(flushWhenIdle)を false に設定できます。これは、以前にフラッシュされた(または新しい)ファイルへの最初の書き込みで間隔が始まることを意味します。
ファイルのタイムスタンプ
デフォルトでは、宛先ファイルの lastModified タイムスタンプは、ファイルが作成された時刻です(ただし、インプレース名前変更では現在のタイムスタンプが保持されます)。バージョン 4.3 から、preserve-timestamp (または Java 構成を使用する場合は setPreserveTimestamp(true))を構成できるようになりました。File ペイロードの場合、これにより、タイムスタンプが受信ファイルから送信に転送されます(コピーが必要かどうかに関係なく)。他のペイロードでは、FileHeaders.SET_MODIFIED ヘッダー(file_setModified)が存在する場合、ヘッダーが Number である限り、宛先ファイルの lastModified タイムスタンプの設定に使用されます。
ファイル許可
バージョン 5.0 から、Posix 許可をサポートするファイルシステムにファイルを書き込むときに、送信チャネルアダプターまたはゲートウェイでそれらの許可を指定できます。このプロパティは整数であり、通常は使い慣れた 8 進数形式で提供されます。たとえば、0640 は、所有者に読み取り / 書き込み権限があり、グループに読み取り専用権限があり、その他にはアクセス権がないことを意味します。
ファイル送信チャネルアダプター
次の例では、ファイル送信チャネルアダプターを構成します。
<int-file:outbound-channel-adapter id="filesOut" directory="${input.directory.property}"/> 名前空間ベースの構成は、delete-source-files 属性もサポートしています。true に設定すると、宛先への書き込み後に元のソースファイルの削除がトリガーされます。そのフラグのデフォルト値は false です。次の例は、true に設定する方法を示しています。
<int-file:outbound-channel-adapter id="filesOut"
directory="${output.directory}"
delete-source-files="true"/>delete-source-files 属性は、受信メッセージに File ペイロードがある場合、または FileHeaders.ORIGINAL_FILE ヘッダー値にソース File インスタンスまたは元のファイルパスを表す String が含まれている場合にのみ効果があります。 |
バージョン 4.2 以降、FileWritingMessageHandler は append-new-line オプションをサポートしています。true に設定されている場合、メッセージが書き込まれた後、ファイルに新しい行が追加されます。デフォルトの属性値は false です。次の例は、append-new-line オプションの使用方法を示しています。
<int-file:outbound-channel-adapter id="newlineAdapter"
append-new-line="true"
directory="${output.directory}"/>送信ゲートウェイ
書き込まれたファイルに基づいてメッセージの処理を続行する場合は、代わりに outbound-gateway を使用できます。outbound-channel-adapter と同様のロールを果たします。ただし、ファイルを書き込んだ後、メッセージのペイロードとして応答チャネルに送信します。
次の例では、送信ゲートウェイを構成します。
<int-file:outbound-gateway id="mover" request-channel="moveInput"
reply-channel="output"
directory="${output.directory}"
mode="REPLACE" delete-source-files="true"/> 前述のように、mode 属性を指定することもできます。これは、宛先ファイルがすでに存在する状況に対処する方法の動作を定義します。詳細については、既存の宛先ファイルの処理を参照してください。一般に、ファイル送信ゲートウェイを使用する場合、結果ファイルは応答チャネルのメッセージペイロードとして返されます。
これは、IGNORE モードを指定する場合にも適用されます。その場合、既存の宛先ファイルが返されます。リクエストメッセージのペイロードがファイルの場合、メッセージヘッダーを介して元のファイルにアクセスできます。FileHeaders.ORIGINAL_FILE (Javadoc) を参照してください。
「送信ゲートウェイ」は、最初にファイルを移動してから処理パイプラインを介して送信する場合に適しています。このような場合、ファイル名前空間の inbound-channel-adapter 要素を outbound-gateway に接続してから、そのゲートウェイの reply-channel をパイプラインの先頭に接続できます。 |
より複雑な要件がある場合、またはファイルコンテンツに変換する入力として追加のペイロード型をサポートする必要がある場合は、FileWritingMessageHandler を継承できますが、はるかに優れたオプションは Transformer に依存することです。
Java 構成を使用した構成
次の Spring Boot アプリケーションは、Java 構成で受信アダプターを構成する方法の例を示しています。
@SpringBootApplication
@IntegrationComponentScan
public class FileWritingJavaApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context =
new SpringApplicationBuilder(FileWritingJavaApplication.class)
.web(false)
.run(args);
MyGateway gateway = context.getBean(MyGateway.class);
gateway.writeToFile("foo.txt", new File(tmpDir.getRoot(), "fileWritingFlow"), "foo");
}
@Bean
@ServiceActivator(inputChannel = "writeToFileChannel")
public MessageHandler fileWritingMessageHandler() {
Expression directoryExpression = new SpelExpressionParser().parseExpression("headers.directory");
FileWritingMessageHandler handler = new FileWritingMessageHandler(directoryExpression);
handler.setFileExistsMode(FileExistsMode.APPEND);
return handler;
}
@MessagingGateway(defaultRequestChannel = "writeToFileChannel")
public interface MyGateway {
void writeToFile(@Header(FileHeaders.FILENAME) String fileName,
@Header(FileHeaders.FILENAME) File directory, String data);
}
}Java DSL を使用した構成
次の Spring Boot アプリケーションは、Java DSL で受信アダプターを構成する方法の例を示しています。
@SpringBootApplication
public class FileWritingJavaApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context =
new SpringApplicationBuilder(FileWritingJavaApplication.class)
.web(false)
.run(args);
MessageChannel fileWritingInput = context.getBean("fileWritingInput", MessageChannel.class);
fileWritingInput.send(new GenericMessage<>("foo"));
}
@Bean
public IntegrationFlow fileWritingFlow() {
return IntegrationFlow.from("fileWritingInput")
.enrichHeaders(h -> h.header(FileHeaders.FILENAME, "foo.txt")
.header("directory", new File(tmpDir.getRoot(), "fileWritingFlow")))
.handle(Files.outboundGateway(m -> m.getHeaders().get("directory")))
.channel(MessageChannels.queue("fileWritingResultChannel"))
.get();
}
}