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 物件列表。這些物件提供修改時間、許可權和其他詳細資訊。
file_remoteDirectory 頭部提供了 ls 命令操作的遠端目錄。
當使用遞迴選項 (-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 操作產生的訊息載荷是檔名列表。
file_remoteDirectory 頭部提供了 nlst 命令操作的遠端目錄。
與 ls 命令的 -1 選項(使用 LIST 命令)不同,nlst 命令向目標 FTP 伺服器傳送 NLST 命令。當伺服器不支援 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 版本開始,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 版本開始,mv 命令可以使用 remoteDirectoryExpression 以方便。如果“源”檔案不是完整的檔案路徑,則 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。