SFTP 出站閘道器
SFTP 出站閘道器提供了一組有限的命令,允許您與遠端 SFTP 伺服器互動
-
ls
(列出檔案) -
nlst
(列出檔名) -
get
(檢索檔案) -
mget
(檢索多個檔案) -
rm
(刪除檔案) -
mv
(移動和重新命名檔案) -
put
(傳送檔案) -
mput
(傳送多個檔案)
使用 ls
命令
ls
命令列出遠端檔案並支援以下選項
-
-1
: 檢索檔名列表。預設是檢索FileInfo
物件列表 -
-a
: 包含所有檔案(包括以 '.' 開頭的檔案) -
-f
: 不對列表進行排序 -
-dirs
: 包含目錄(預設排除) -
-links
: 包含符號連結(預設排除) -
-R
: 遞迴列出遠端目錄
此外,還提供了與 inbound-channel-adapter
類似的檔名過濾功能。
ls
操作產生的訊息有效載荷是檔名列表或 FileInfo
物件列表(取決於您是否使用 -1
選項)。這些物件提供修改時間、許可權等資訊。
ls
命令操作的遠端目錄會放在 file_remoteDirectory
頭部。
使用遞迴選項 (-R
) 時,fileName
會包含任何子目錄元素,並表示檔案的相對路徑(相對於遠端目錄)。如果您使用 -dirs
選項,每個遞迴目錄也會作為列表中的一個元素返回。在這種情況下,我們建議您不要使用 -1
選項,因為那樣您將無法區分檔案和目錄,而使用 FileInfo
物件時可以做到這一點。
如果列出的遠端路徑以 /
符號開頭,SFTP 會將其視為絕對路徑;否則,將其視為當前使用者主目錄中的相對路徑。
使用 nlst
命令
版本 5 引入了對 nlst
命令的支援。
nlst
列出遠端檔名並僅支援一個選項
-
-f
: 不對列表進行排序
nlst
操作產生的訊息有效載荷是檔名列表。
file_remoteDirectory
頭部儲存了 nlst
命令操作的遠端目錄。
SFTP 協議本身不提供列出名稱的功能。此命令等同於帶有 -1
選項的 ls
命令,此處新增是為了方便。
使用 get
命令
get
檢索遠端檔案並支援以下選項
-
-P
: 保留遠端檔案的時間戳。 -
-stream
: 將遠端檔案檢索為流。 -
-D
: 傳輸成功後刪除遠端檔案。如果傳輸被忽略,則不刪除遠端檔案,因為FileExistsMode
為IGNORE
且本地檔案已存在。
file_remoteDirectory
頭部儲存遠端目錄,file_remoteFile
頭部儲存檔名。
get
操作產生的訊息有效載荷是一個表示已檢索檔案的 File
物件。如果您使用 -stream
選項,有效載荷將是 InputStream
而不是 File
。對於文字檔案,一個常見的使用場景是將此操作與檔案拆分器或流轉換器結合使用。將遠端檔案作為流消費時,您有責任在流消費後關閉 Session
。為了方便,Session
會提供在 closeableResource
頭部,並且 IntegrationMessageHeaderAccessor
提供了便捷方法
Closeable closeable = new IntegrationMessageHeaderAccessor(message).getCloseableResource();
if (closeable != null) {
closeable.close();
}
以下示例展示瞭如何將檔案作為流進行消費
<int-sftp: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 ,輸出訊息的有效載荷將不再包含因檔案已存在而未獲取的檔案。之前,陣列包含所有檔案,包括已存在的檔案。 |
您用於確定遠端路徑的表示式應該產生一個以 *
結尾的結果,例如 myfiles/*
會獲取 myfiles
下的完整樹。
從版本 5.0 開始,您可以結合遞迴 MGET
和 FileExistsMode.REPLACE_IF_MODIFIED
模式,定期將整個遠端目錄樹同步到本地。此模式會將本地檔案的最後修改時間戳設定為遠端檔案的時間戳,而不管 -P
(保留時間戳)選項如何。
使用遞迴 (
-R ) 時的注意事項模式將被忽略,並假定為 如果您過濾一個子目錄,則不會對該子目錄進行額外的遍歷。 不允許使用 通常,您會在 |
持久化檔案列表過濾器現在具有布林屬性 forRecursion
。將此屬性設定為 true
,也會設定 alwaysAcceptDirectories
,這意味著出站閘道器(ls
和 mget
)上的遞迴操作現在每次都會始終遍歷完整的目錄樹。這是為了解決未檢測到目錄樹深層變化的問題。此外,forRecursion=true
會導致使用檔案的完整路徑作為元資料儲存鍵;這解決了如果相同名稱的檔案出現在不同目錄中多次時過濾器無法正常工作的問題。重要提示:這意味著對於頂級目錄下的檔案,持久化元資料儲存中的現有鍵將無法找到。因此,該屬性預設為 false
;這可能會在未來版本中更改。
從版本 5.0 開始,您可以透過將 alwaysAcceptDirectorties
設定為 true
來配置 SftpSimplePatternFileListFilter
和 SftpRegexPatternFileListFilter
始終透過目錄。這樣做允許簡單模式下的遞迴,如下例所示
<bean id="starDotTxtFilter"
class="org.springframework.integration.sftp.filters.SftpSimplePatternFileListFilter">
<constructor-arg value="*.txt" />
<property name="alwaysAcceptDirectories" value="true" />
</bean>
<bean id="dotStarDotTxtFilter"
class="org.springframework.integration.sftp.filters.SftpRegexPatternFileListFilter">
<constructor-arg value="^.*\.txt$" />
<property name="alwaysAcceptDirectories" value="true" />
</bean>
您可以透過在閘道器上使用 filter
屬性來提供這些過濾器之一。
使用 put
命令
put
將檔案傳送到遠端伺服器。訊息的有效載荷可以是 java.io.File
、byte[]
或 String
。使用 remote-filename-generator
(或表示式)來命名遠端檔案。其他可用屬性包括 remote-directory
、temporary-remote-directory
及其 *-expression
等價物:use-temporary-file-name
和 auto-create-directory
。有關詳細資訊,請參見schema 文件。
put
操作產生的訊息有效載荷是一個 String
,其中包含傳輸後文件在伺服器上的完整路徑。
版本 4.3 引入了 chmod
屬性,它在上傳後更改遠端檔案許可權。您可以使用傳統的 Unix 八進位制格式(例如,600
僅允許檔案所有者讀寫)。使用 Java 配置介面卡時,可以使用 setChmod(0600)
。
使用 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
)。
版本 4.3 引入了 chmod
屬性,它允許您在上傳後更改遠端檔案許可權。您可以使用傳統的 Unix 八進位制格式(例如,600
僅允許檔案所有者讀寫)。使用 Java 配置介面卡時,可以使用 setChmodOctal("600")
或 setChmod(0600)
。
使用 rm
命令
rm
命令沒有選項。
如果刪除操作成功,結果訊息有效載荷為 Boolean.TRUE
。否則,訊息有效載荷為 Boolean.FALSE
。file_remoteDirectory
頭部儲存遠端目錄,file_remoteFile
頭部儲存檔名。
使用 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
的結果將用作遠端目錄。這同樣適用於“目標”檔案,例如,如果任務只是重新命名某個目錄中的遠端檔案。
附加命令資訊
get
和 mget
命令支援 local-filename-generator-expression
屬性。它定義了一個 SpEL 表示式,用於在傳輸過程中生成本地檔案的名稱。評估上下文的根物件是請求訊息。remoteFileName
變數也可用。它對 mget
特別有用(例如:local-filename-generator-expression="#remoteFileName.toUpperCase() + headers.foo"
)。
get
和 mget
命令支援 local-directory-expression
屬性。它定義了一個 SpEL 表示式,用於在傳輸過程中生成本地目錄的名稱。評估上下文的根物件是請求訊息。remoteDirectory
變數也可用。它對 mget 特別有用(例如:local-directory-expression="'/tmp/local/' + #remoteDirectory.toUpperCase() + headers.myheader"
)。此屬性與 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="-1"
,有效載荷將是 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
。
使用 Java 配置進行配置
以下 Spring Boot 應用展示瞭如何使用 Java 配置出站閘道器的示例
@SpringBootApplication
public class SftpJavaApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(SftpJavaApplication.class)
.web(false)
.run(args);
}
@Bean
@ServiceActivator(inputChannel = "sftpChannel")
public MessageHandler handler() {
return new SftpOutboundGateway(ftpSessionFactory(), "ls", "'my_remote_dir/'");
}
}
使用 Java DSL 進行配置
以下 Spring Boot 應用展示瞭如何使用 Java DSL 配置出站閘道器的示例
@SpringBootApplication
public class SftpJavaApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(SftpJavaApplication.class)
.web(false)
.run(args);
}
@Bean
public SessionFactory<SftpClient.DirEntry> sftpSessionFactory() {
DefaultSftpSessionFactory sf = new DefaultSftpSessionFactory();
sf.setHost("localhost");
sf.setPort(port);
sf.setUsername("foo");
sf.setPassword("foo");
factory.setTestSession(true);
return new CachingSessionFactory<>(sf);
}
@Bean
public QueueChannelSpec remoteFileOutputChannel() {
return MessageChannels.queue();
}
@Bean
public IntegrationFlow sftpMGetFlow() {
return IntegrationFlow.from("sftpMgetInputChannel")
.handle(Sftp.outboundGateway(sftpSessionFactory(),
AbstractRemoteFileOutboundGateway.Command.MGET, "payload")
.options(AbstractRemoteFileOutboundGateway.Option.RECURSIVE)
.regexFileNameFilter("(subSftpSource|.*1.txt)")
.localDirectoryExpression("'myDir/' + #remoteDirectory")
.localFilenameExpression("#remoteFileName.replaceFirst('sftpSource', 'localTarget')"))
.channel("remoteFileOutputChannel")
.get();
}
}
出站閘道器部分成功(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
。