FTP 出站閘道器
FTP 出站閘道器提供了一組有限的命令,用於與遠端 FTP 或 FTPS 伺服器互動。支援的命令有:
-
ls
(列出檔案) -
nlst
(列出檔名) -
get
(檢索檔案) -
mget
(檢索檔案(s)) -
rm
(刪除檔案(s)) -
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
命令,根據 FTP 協議,null
被視為客戶端工作目錄。所有其他命令必須提供 expression
以根據請求訊息評估遠端路徑。當您擴充套件 DefaultFtpSessionFactory
並實現 postProcessClientAfterConnect()
回撥時,可以使用 FTPClient.changeWorkingDirectory()
函式設定工作目錄。
使用 nlst
命令
版本 5 引入了對 nlst
命令的支援。
nlst
列出遠端檔名,僅支援一個選項:
-
-f
:不排序列表。
nlst
操作產生的訊息負載是檔名稱列表。
nlst
命令操作的遠端目錄在 file_remoteDirectory
訊息頭中提供。
與使用 LIST
命令的 ls
命令的 -1
選項不同,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
物件列表,每個物件表示一個檢索到的檔案)。
從版本 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 開始,透過將 alwaysAcceptDirectories
屬性設定為 true
,可以配置 FtpSimplePatternFileListFilter
和 FtpRegexPatternFileListFilter
以始終允許目錄透過。這樣做允許對簡單模式進行遞迴,如下例所示:
<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
(或 expression)用於命名遠端檔案。其他可用屬性包括 remote-directory
、temporary-remote-directory
及其 *-expression
等效屬性:use-temporary-file-name
和 auto-create-directory
。有關更多資訊,請參見schema 文件。
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>
物件(即一個遠端檔案路徑列表,是傳輸結果)。
另請參閱 出站閘道器部分成功 (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 表示式,用於在傳輸期間生成本地檔案的名稱。評估上下文的根物件是請求訊息。對於 mget
特別有用的 remoteFileName
變數也可用,例如 local-filename-generator-expression="#remoteFileName.toUpperCase() + headers.something"
。
get
和 mget
命令支援 local-directory-expression
屬性。它定義了一個 SpEL 表示式,用於在傳輸期間生成本地目錄的名稱。評估上下文的根物件是請求訊息,但對於 mget
特別有用的 remoteDirectory
變數也可用,例如:local-directory-expression="'/tmp/local/' + #remoteDirectory.toUpperCase() + headers.something"
。此屬性與 local-directory
屬性互斥。
對於所有命令,閘道器的 'expression' 屬性提供了命令作用的路徑。對於 mget
命令,expression 可能計算為 '*
',表示檢索所有檔案,或 '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
上提供了 setWorkingDirExpression()
(XML 中為 working-dir-expression
)選項(XML 中為 <int-ftp:outbound-gateway>
)。它允許您在執行時更改客戶端工作目錄。表示式根據請求訊息進行評估。在每次閘道器操作後,會恢復之前的工作目錄。
使用 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
。