寫入檔案
若要將訊息寫入檔案系統,您可以使用 FileWritingMessageHandler
。此類別可以處理以下酬載類型
-
File
-
String
-
byte array
-
InputStream
(自版本 4.2起)
對於 String 酬載,您可以設定編碼和字元集。
為了簡化操作,您可以使用 XML 命名空間將 FileWritingMessageHandler
設定為輸出通道配接器或輸出閘道器的一部分。
從版本 4.3 開始,您可以指定寫入檔案時要使用的緩衝區大小。
從版本 5.1 開始,您可以提供 BiConsumer<File, Message<?>>
newFileCallback
,如果您使用 FileExistsMode.APPEND
或 FileExistsMode.APPEND_NO_FLUSH
且必須建立新檔案,則會觸發此回呼。此回呼會接收新建立的檔案和觸發它的訊息。此回呼可用於寫入訊息標頭中定義的 CSV 標頭,例如。
產生檔案名稱
在其最簡單的形式中,FileWritingMessageHandler
只需要一個用於寫入檔案的目的地目錄。要寫入的檔案名稱由處理器的 FileNameGenerator
決定。預設實作 會尋找訊息標頭,其鍵符合定義為 FileHeaders.FILENAME
的常數。
或者,您可以指定一個運算式,針對訊息進行評估以產生檔案名稱,例如 headers['myCustomHeader'] + '.something'
。運算式必須評估為 String
。為了方便起見,DefaultFileNameGenerator
也提供了 setHeaderName
方法,讓您可以明確指定要將其值用作檔案名稱的訊息標頭。
設定完成後,DefaultFileNameGenerator
會採用以下解析步驟來判斷給定訊息酬載的檔案名稱
-
針對訊息評估運算式,如果結果是非空
String
,則將其用作檔案名稱。 -
否則,如果酬載是
java.io.File
,則使用File
物件的檔案名稱。 -
否則,使用附加 .
msg
的訊息 ID 作為檔案名稱。
當您使用 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)
的第二個建構子引數。但是,過去我們沒有為子路徑建立 (mkdirs()
) 目錄,假設只有檔案名稱。當我們需要還原檔案系統樹以符合來源目錄時,此方法很有用,例如,當解壓縮檔案並將所有檔案以原始順序儲存在目標目錄中時。
指定輸出目錄
檔案輸出通道配接器和檔案輸出閘道器都提供兩個互斥的設定屬性,用於指定輸出目錄
-
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
-
REPLACE_IF_MODIFIED
APPEND
-
如果目標檔案已存在,則僅當上次修改的時間戳記與來源檔案的時間戳記不同時,才會覆寫它。對於
File
酬載,酬載lastModified
時間會與現有檔案進行比較。對於其他酬載,FileHeaders.SET_MODIFIED
(file_setModified
) 標頭會與現有檔案進行比較。如果標頭遺失或值不是Number
,則始終會替換檔案。 APPEND_NO_FLUSH
-
APPEND
FAIL
-
此模式可讓您將訊息內容附加到現有檔案,而不是每次都建立新檔案。請注意,此屬性與
temporary-file-suffix
屬性互斥,因為當它將內容附加到現有檔案時,配接器不再使用臨時檔案。檔案會在每個訊息後關閉。 IGNORE
-
APPEND_NO_FLUSH
此選項具有與 APPEND 相同的語意,但資料不會刷新,且檔案不會在每個訊息後關閉。這可以提供顯著的效能,但存在失敗時資料遺失的風險。如需更多資訊,請參閱 使用 APPEND_NO_FLUSH 時刷新檔案。 |
FAIL
如果目標檔案存在,則會擲回 MessageHandlingException
。
IGNORE
Spring Integration 提供了幾種刷新策略來減輕這種資料遺失
使用 flushInterval
。如果檔案在此期間內未寫入,則會自動刷新。這是近似值,可能最多為此時間的 1.33 倍(平均為 1.167 倍)。
將包含正則運算式的訊息傳送到訊息處理器的 trigger
方法。絕對路徑名稱與模式相符的檔案將被刷新。
為處理器提供自訂 MessageFlushPredicate
實作,以修改將訊息傳送到 trigger
方法時採取的動作。
透過傳入自訂 FileWritingMessageHandler.FlushPredicate
或 FileWritingMessageHandler.MessageFlushPredicate
實作,叫用處理器的 flushIfNeeded
方法之一。
針對每個開啟的檔案呼叫述詞。如需更多資訊,請參閱這些介面的 Javadoc。請注意,自版本 5.0 起,述詞方法提供了另一個參數:目前檔案首次寫入的時間(如果是新檔案或先前已關閉的檔案)。
當使用 flushInterval
時,間隔從上次寫入開始。僅當檔案在間隔內處於閒置狀態時才會刷新。從版本 4.3.7 開始,可以將其他屬性 (flushWhenIdle
) 設定為 false
,這表示間隔從首次寫入先前刷新的(或新的)檔案開始。
<int-file:outbound-channel-adapter id="filesOut" directory="${input.directory.property}"/>
預設情況下,目的地檔案的 lastModified
時間戳記是檔案建立的時間(就地重新命名會保留目前時間戳記除外)。從版本 4.3 開始,您現在可以設定 preserve-timestamp
(或在使用 Java 設定時設定 setPreserveTimestamp(true)
)。對於 File
酬載,這會將時間戳記從輸入檔案傳輸到輸出檔案(無論是否需要複製)。對於其他酬載,如果 FileHeaders.SET_MODIFIED
標頭 (file_setModified
) 存在,則只要標頭是 Number
,就會使用它來設定目的地檔案的 lastModified
時間戳記。
<int-file:outbound-channel-adapter id="filesOut"
directory="${output.directory}"
delete-source-files="true"/>
從版本 5.0 開始,當將檔案寫入支援 Posix 權限的檔案系統時,您可以在輸出通道配接器或閘道器上指定這些權限。該屬性是整數,通常以熟悉的八進位格式提供,例如 0640
,表示擁有者具有讀/寫權限,群組具有唯讀權限,而其他人則沒有存取權。
<int-file:outbound-channel-adapter id="newlineAdapter"
append-new-line="true"
directory="${output.directory}"/>
檔案輸出通道配接器
以下範例設定檔案輸出通道配接器
基於命名空間的設定也支援 delete-source-files
屬性。如果設定為 true
,則會在寫入目的地後觸發刪除原始來源檔案。該標誌的預設值為 false
。以下範例示範如何將其設定為 true
<int-file:outbound-gateway id="mover" request-channel="moveInput"
reply-channel="output"
directory="${output.directory}"
mode="REPLACE" delete-source-files="true"/>
delete-source-files
屬性僅在輸入訊息具有 File
酬載,或 FileHeaders.ORIGINAL_FILE
標頭值包含來源 File
實例或表示原始檔案路徑的 String
時才生效。
從版本 4.2 開始,FileWritingMessageHandler
支援 append-new-line
選項。如果設定為 true
,則在寫入訊息後,會在檔案末尾附加新行。預設屬性值為 false
。以下範例示範如何使用 append-new-line
選項
在您想要根據寫入的檔案繼續處理訊息的情況下,可以使用 outbound-gateway
來代替。它扮演的角色與 outbound-channel-adapter
類似。但是,在寫入檔案後,它也會將其作為訊息的酬載傳送到回覆通道。
以下範例設定輸出閘道器
如前所述,您也可以指定 mode
屬性,該屬性定義如何處理目的地檔案已存在的情況的行為。如需更多詳細資訊,請參閱處理現有的目的地檔案。一般而言,當使用檔案輸出閘道器時,結果檔案會作為回覆通道上的訊息酬載傳回。
@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);
}
}
這也適用於指定 IGNORE
模式時。在這種情況下,會傳回預先存在目的地檔案。如果請求訊息的酬載是檔案,您仍然可以透過訊息標頭存取該原始檔案。請參閱 FileHeaders.ORIGINAL_FILE。
outbound-gateway
在您想要先移動檔案,然後將其透過處理管線傳送的情況下效果良好。在這種情況下,您可以將檔案命名空間的 inbound-channel-adapter
元素連線到 outbound-gateway
,然後將該閘道器的 reply-channel
連線到管線的開頭。
@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();
}
}