ファイルを書く
ファイルシステムにメッセージを書き込むには、FileWritingMessageHandler
(Javadoc) を使用できます。このクラスは、次のペイロード型を処理できます。
File
String
Byte 配列
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 つの構成属性を提供します。
directory
directory-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_MODIFIED
APPEND
APPEND_NO_FLUSH
FAIL
IGNORE
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();
}
}