FTP 輸出閘道
FTP 輸出閘道提供一組有限的指令,用於與遠端 FTP 或 FTPS 伺服器互動。支援的指令如下:
-
ls
(列出檔案) -
nlst
(列出檔案名稱) -
get
(擷取檔案) -
mget
(擷取檔案) -
rm
(移除檔案) -
mv
(移動/重新命名檔案) -
put
(傳送檔案) -
mput
(傳送多個檔案)
使用 ls
指令
ls
列出遠端檔案並支援以下選項
-
-1
:擷取檔案名稱清單。預設為擷取FileInfo
物件清單。 -
-a
:包含所有檔案 (包括以 '.' 開頭的檔案) -
-f
:不排序清單 -
-dirs
:包含目錄 (預設排除) -
-links
:包含符號連結 (預設排除) -
-R
:遞迴列出遠端目錄
此外,也提供檔案名稱篩選,方式與 inbound-channel-adapter
相同。請參閱 FTP 輸入通道配接器。
ls
作業產生的訊息酬載是檔案名稱清單或 FileInfo
物件清單。這些物件提供修改時間、權限和其他詳細資訊等資訊。
ls
指令作用的遠端目錄會在 file_remoteDirectory
標頭中提供。
使用遞迴選項 (-R
) 時,fileName
會包含任何子目錄元素,代表檔案的相對路徑 (相對於遠端目錄)。如果包含 -dirs
選項,則每個遞迴目錄也會以清單中的元素傳回。在這種情況下,建議您不要使用 -1
選項,因為您將無法區分檔案和目錄,而使用 FileInfo
物件則可以區分。
從 4.3 版開始,FtpSession
支援 list()
和 listNames()
方法的 null
值。因此,您可以省略 expression
屬性。為了方便起見,Java 設定有兩個不含 expression
引數的建構函式。或 LS
、NLST
、PUT
和 MPUT
指令,null
會根據 FTP 協定視為用戶端工作目錄。所有其他指令都必須提供 expression
,以針對請求訊息評估遠端路徑。當您擴充 DefaultFtpSessionFactory
並實作 postProcessClientAfterConnect()
回呼時,可以使用 FTPClient.changeWorkingDirectory()
函數設定工作目錄。
使用 nlst
指令
版本 5 引入了對 nlst
指令的支援。
nlst
列出遠端檔案名稱,且僅支援一個選項
-
-f
:不排序清單
nlst
作業產生的訊息酬載是檔案名稱清單。
nlst
指令作用的遠端目錄會在 file_remoteDirectory
標頭中提供。
與 ls
指令 的 -1
選項 (使用 LIST
指令) 不同,nlst
指令會將 NLST
指令傳送至目標 FTP 伺服器。當伺服器不支援 LIST
時 (例如,由於安全性限制),此指令很有用。nlst
作業的結果是不包含其他詳細資訊的名稱。因此,框架無法判斷實體是否為目錄,以執行篩選或遞迴清單等作業。
使用 get
指令
get
擷取遠端檔案。它支援以下選項
-
-P
:保留遠端檔案的時間戳記。 -
-stream
:將遠端檔案擷取為串流。 -
-D
:在成功傳輸後刪除遠端檔案。如果傳輸遭到忽略,則不會刪除遠端檔案,因為FileExistsMode
為IGNORE
且本機檔案已存在。
file_remoteDirectory
標頭提供遠端目錄名稱,而 file_remoteFile
標頭提供檔案名稱。
get
作業產生的訊息酬載是代表擷取檔案的 File
物件,或是在您使用 -stream
選項時為 InputStream
。-stream
選項允許將檔案擷取為串流。對於文字檔,常見的用例是將此作業與檔案分割器或串流轉換器結合使用。當以串流方式取用遠端檔案時,您有責任在串流取用後關閉 Session
。為了方便起見,Session
在 closeableResource
標頭中提供,您可以使用 IntegrationMessageHeaderAccessor
上的便利方法存取。以下範例示範如何使用便利方法
Closeable closeable = new IntegrationMessageHeaderAccessor(message).getCloseableResource();
if (closeable != null) {
closeable.close();
}
以下範例示範如何以串流方式取用檔案
<int-ftp: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 ,則輸出訊息的酬載不再包含由於檔案已存在而未擷取的檔案。先前,清單包含所有檔案,包括已存在的檔案。 |
用於判斷遠端路徑的運算式應產生以 結尾的結果 - 例如,
somedir/
將擷取 somedir
下的完整樹狀結構。
從 5.0 版開始,遞迴 mget
與新的 FileExistsMode.REPLACE_IF_MODIFIED
模式結合使用,可用於定期在本機同步整個遠端目錄樹狀結構。無論 -P
(保留時間戳記) 選項為何,此模式都會以遠端時間戳記取代本機檔案的上次修改時間戳記。
使用遞迴 (
-R )模式會遭到忽略,且假設為 如果篩選了子目錄,則不會執行該子目錄的其他遍歷。 不允許使用 通常,您會在 |
持久檔案清單篩選器現在具有布林值屬性 forRecursion
。將此屬性設定為 true
,也會設定 alwaysAcceptDirectories
,這表示輸出閘道 (ls
和 mget
) 上的遞迴作業現在每次都會遍歷完整的目錄樹狀結構。這是為了解決目錄樹狀結構深處的變更未偵測到的問題。此外,forRecursion=true
會導致完整檔案路徑用作中繼資料儲存區金鑰;這解決了如果具有相同名稱的檔案在不同目錄中多次出現,篩選器無法正常運作的問題。重要事項:這表示在持久中繼資料儲存區中的現有金鑰將不會針對最上層目錄下方的檔案找到。基於這個原因,此屬性預設為 false
;這可能會在未來的版本中變更。
從 5.0 版開始,可以設定 FtpSimplePatternFileListFilter
和 FtpRegexPatternFileListFilter
,方法是將 alwaysAcceptDirectories
屬性設定為 true
,以始終傳遞目錄。這樣做可讓簡單模式進行遞迴,如下列範例所示
<bean id="starDotTxtFilter"
class="org.springframework.integration.ftp.filters.FtpSimplePatternFileListFilter">
<constructor-arg value="*.txt" />
<property name="alwaysAcceptDirectories" value="true" />
</bean>
<bean id="dotStarDotTxtFilter"
class="org.springframework.integration.ftp.filters.FtpRegexPatternFileListFilter">
<constructor-arg value="^.*\.txt$" />
<property name="alwaysAcceptDirectories" value="true" />
</bean>
定義如先前範例中的篩選器後,您可以透過在閘道上設定 filter
屬性來使用其中一個篩選器。
另請參閱 輸出閘道部分成功 (mget
和 mput
)。
使用 put
指令
put
指令會將檔案傳送至遠端伺服器。訊息的酬載可以是 java.io.File
、byte[]
或 String
。remote-filename-generator
(或運算式) 用於命名遠端檔案。其他可用的屬性包括 remote-directory
、temporary-remote-directory
及其 *-expression
對等項目:use-temporary-file-name
和 auto-create-directory
。請參閱 結構描述 文件以取得更多資訊。
put
作業產生的訊息酬載是 String
,代表傳輸後伺服器上檔案的完整路徑。
版本 5.2 引入了 chmod
屬性,可在上傳後變更遠端檔案權限。您可以使用傳統的 Unix 八進位格式 (例如,600
僅允許檔案擁有者讀寫)。使用 java 設定配接器時,可以使用 setChmod(0600)
。僅在您的 FTP 伺服器支援 SITE CHMOD
子指令時適用。
使用 mput
指令
mput
會將多個檔案傳送至伺服器,且僅支援一個選項
-
-R
:遞迴。傳送目錄及其子目錄中的所有檔案 (可能已篩選)。
訊息酬載必須是 java.io.File
(或 String
),代表本機目錄。從 5.1 版開始,也支援 File
或 String
的集合。
此指令支援與 put
指令 相同的屬性。此外,可以使用 mput-pattern
、mput-regex
、mput-filter
或 mput-filter-expression
之一篩選本機目錄中的檔案。只要子目錄本身通過篩選器,篩選器就會與遞迴一起運作。未通過篩選器的子目錄不會遞迴。
mput
作業產生的訊息酬載是 List<String>
物件 (亦即,傳輸產生的遠端檔案路徑 List
)。
另請參閱 輸出閘道部分成功 (mget
和 mput
)。
版本 5.2 引入了 chmod
屬性,可讓您在上傳後變更遠端檔案權限。您可以使用傳統的 Unix 八進位格式 (例如,600
僅允許檔案擁有者讀寫)。使用 Java 設定配接器時,可以使用 setChmodOctal("600")
或 setChmod(0600)
。僅在您的 FTP 伺服器支援 SITE CHMOD
子指令時適用。
使用 rm
指令
rm
指令會移除檔案。
rm
指令沒有選項。
如果移除成功,rm
作業產生的訊息酬載為 Boolean.TRUE
,否則為 Boolean.FALSE
。file_remoteDirectory
標頭提供遠端目錄,而 file_remoteFile
標頭提供檔案名稱。
使用 mv
指令
mv
指令會移動檔案。
mv
指令沒有選項。
expression
屬性定義「來源」路徑,而 rename-expression
屬性定義「目標」路徑。預設情況下,rename-expression
為 headers['file_renameTo']
。此運算式不得評估為 null 或空白 String
。如有必要,將建立任何必要的遠端目錄。結果訊息的酬載為 Boolean.TRUE
。file_remoteDirectory
標頭提供原始遠端目錄,而 file_remoteFile
標頭提供檔案名稱。新路徑位於 file_renameTo
標頭中。
從 5.5.6 版開始,remoteDirectoryExpression
可在 mv
指令中方便使用。如果「來源」檔案不是完整檔案路徑,則 remoteDirectoryExpression
的結果會用作遠端目錄。這同樣適用於「目標」檔案,例如,如果任務只是重新命名某個目錄中的遠端檔案。
關於 FTP 輸出閘道指令的其他資訊
get
和 mget
指令支援 local-filename-generator-expression
屬性。它定義 SpEL 運算式,以在傳輸期間產生本機檔案的名稱。評估內容的根物件是請求訊息。remoteFileName
變數 (對於 mget
特別有用) 也可用 - 例如,local-filename-generator-expression="#remoteFileName.toUpperCase() + headers.something"
。
get
和 mget
指令支援 local-directory-expression
屬性。它定義 SpEL 運算式,以在傳輸期間產生本機目錄的名稱。評估內容的根物件是請求訊息,但 remoteDirectory
變數 (對於 mget
特別有用) 也可用 - 例如:local-directory-expression="'/tmp/local/' + #remoteDirectory.toUpperCase() + headers.something"
。此屬性與 local-directory
屬性互斥。
對於所有指令,閘道的 'expression' 屬性提供指令作用的路徑。對於 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
屬性,則會保留 FileInfo
物件。它使用空格分隔的選項 - 例如,command-options="-1 -dirs -links"
。
從 4.2 版開始,GET
、MGET
、PUT
和 MPUT
指令支援 FileExistsMode
屬性 (使用命名空間支援時為 mode
)。這會影響本機檔案存在 (GET
和 MGET
) 或遠端檔案存在 (PUT
和 MPUT
) 時的行為。支援的模式為 REPLACE
、APPEND
、FAIL
和 IGNORE
。為了向後相容性,PUT
和 MPUT
作業的預設模式為 REPLACE
。對於 GET
和 MGET
作業,預設為 FAIL
。
從 5.0 版本開始,`FtpOutboundGateway` (XML 中的 <int-ftp:outbound-gateway>
) 上提供了 setWorkingDirExpression()
(XML 中的 working-dir-expression
) 選項。它讓您可以在執行時期變更用戶端工作目錄。此運算式會針對請求訊息進行評估。先前的工作目錄會在每次閘道器操作後還原。
使用 Java 配置進行設定
以下 Spring Boot 應用程式展示了如何使用 Java 配置來設定輸出閘道器的範例
@SpringBootApplication
public class FtpJavaApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(FtpJavaApplication.class)
.web(false)
.run(args);
}
@Bean
public SessionFactory<FTPFile> ftpSessionFactory() {
DefaultFtpSessionFactory sf = new DefaultFtpSessionFactory();
sf.setHost("localhost");
sf.setPort(port);
sf.setUsername("foo");
sf.setPassword("foo");
sf.setTestSession(true);
return new CachingSessionFactory<FTPFile>(sf);
}
@Bean
@ServiceActivator(inputChannel = "ftpChannel")
public MessageHandler handler() {
FtpOutboundGateway ftpOutboundGateway =
new FtpOutboundGateway(ftpSessionFactory(), "ls", "'my_remote_dir/'");
ftpOutboundGateway.setOutputChannelName("lsReplyChannel");
return ftpOutboundGateway;
}
}
使用 Java DSL 進行設定
以下 Spring Boot 應用程式展示了如何使用 Java DSL 來設定輸出閘道器的範例
@SpringBootApplication
public class FtpJavaApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(FtpJavaApplication.class)
.web(false)
.run(args);
}
@Bean
public SessionFactory<FTPFile> ftpSessionFactory() {
DefaultFtpSessionFactory sf = new DefaultFtpSessionFactory();
sf.setHost("localhost");
sf.setPort(port);
sf.setUsername("foo");
sf.setPassword("foo");
sf.setTestSession(true);
return new CachingSessionFactory<FTPFile>(sf);
}
@Bean
public FtpOutboundGatewaySpec ftpOutboundGateway() {
return Ftp.outboundGateway(ftpSessionFactory(),
AbstractRemoteFileOutboundGateway.Command.MGET, "payload")
.options(AbstractRemoteFileOutboundGateway.Option.RECURSIVE)
.regexFileNameFilter("(subFtpSource|.*1.txt)")
.localDirectoryExpression("'localDirectory/' + #remoteDirectory")
.localFilenameExpression("#remoteFileName.replaceFirst('ftpSource', 'localTarget')");
}
@Bean
public IntegrationFlow ftpMGetFlow(AbstractRemoteFileOutboundGateway<FTPFile> ftpOutboundGateway) {
return f -> f
.handle(ftpOutboundGateway)
.channel(c -> c.queue("remoteFileOutputChannel"));
}
}
輸出閘道器部分成功 (mget
和 mput
)
當您對多個檔案執行操作 (透過使用 mget
和 mput
) 時,在一個或多個檔案傳輸後的一段時間,可能會發生例外狀況。在這種情況下 (從 4.2 版本開始),會拋出 PartialSuccessException
。除了常見的 MessagingException
屬性 (failedMessage
和 cause
) 之外,此例外狀況還有兩個額外屬性
-
partialResults
: 成功傳輸的結果。 -
derivedInput
: 從請求訊息產生的檔案列表 (例如,用於mput
的本地檔案傳輸)。
這些屬性讓您可以判斷哪些檔案已成功傳輸,哪些檔案未成功傳輸。
在遞迴 mput
的情況下,PartialSuccessException
可能會有巢狀的 PartialSuccessException
發生。
考慮以下目錄結構
root/
|- file1.txt
|- subdir/
| - file2.txt
| - file3.txt
|- zoo.txt
如果例外狀況發生在 file3.txt
上,則閘道器拋出的 PartialSuccessException
的 derivedInput
為 file1.txt
、subdir
和 zoo.txt
,而 partialResults
為 file1.txt
。其 cause
是另一個 PartialSuccessException
,其 derivedInput
為 file2.txt
和 file3.txt
,而 partialResults
為 file2.txt
。