SMB 支援

Spring Integration 提供對使用 SMB 進行檔案傳輸操作的支援。

Server Message Block (SMB) 是一種簡單的網路協議,允許您將檔案傳輸到共享檔案伺服器。

您需要在專案中包含此依賴項:

  • Maven

  • Gradle

<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-smb</artifactId>
    <version>6.4.4</version>
</dependency>
compile "org.springframework.integration:spring-integration-smb:6.4.4"

概述

我們選擇 Java CIFS 客戶端庫作為 CIFS/SMB 網路協議的 Java 實現。它的 SmbFile 抽象被簡單地封裝到 Spring Integration 的“遠端檔案”基礎中,例如 SmbSessionSmbRemoteFileTemplate 等。

SMB 通道介面卡和支援類實現與現有的 (S)FTP 或 AWS S3 協議元件完全相似。因此,如果您熟悉這些元件,使用起來非常簡單。

Spring Integration 透過提供三種客戶端端點來支援透過 SMB 傳送和接收檔案:入站通道介面卡、出站通道介面卡和出站閘道器。它還提供了方便的基於名稱空間的配置選項來定義這些客戶端元件。

要使用 SMB 名稱空間,請將以下內容新增到 XML 檔案頭部:

xmlns:int-smb="http://www.springframework.org/schema/integration/smb"
xsi:schemaLocation="http://www.springframework.org/schema/integration/smb
    https://www.springframework.org/schema/integration/smb/spring-integration-smb.xsd"

SMB 會話工廠

在配置 SMB 介面卡之前,必須配置一個 SMB 會話工廠。您可以使用常規的 Bean 定義來配置 SMB 會話工廠,如下例所示:

SmbSessionFactory 暴露了設定 SMB 協議最小/最大版本的選項。例如,支援最低 SMB 版本 2.1 和最高 SMB 版本 3.1.1。

@Bean
public SmbSessionFactory smbSessionFactory() {
    SmbSessionFactory smbSession = new SmbSessionFactory();
    smbSession.setHost("myHost");
    smbSession.setPort(445);
    smbSession.setDomain("myDomain");
    smbSession.setUsername("myUser");
    smbSession.setPassword("myPassword");
    smbSession.setShareAndDir("myShareAndDir");
    smbSession.setSmbMinVersion(DialectVersion.SMB210);
    smbSession.setSmbMaxVersion(DialectVersion.SMB311);
    return smbSession;
}

SmbSessionFactory 可以使用自定義的 jcifs.CIFSContext 進行初始化。

SMB 協議最小/最大版本的設定必須在您實現 jcifs.CIFSContext 中完成。
@Bean
public SmbSessionFactory smbSessionFactory() {
    SmbSessionFactory smbSession = new SmbSessionFactory(new MyCIFSContext());
    smbSession.setHost("myHost");
    smbSession.setPort(445);
    smbSession.setDomain("myDomain");
    smbSession.setUsername("myUser");
    smbSession.setPassword("myPassword");
    smbSession.setShareAndDir("myShareAndDir");
    return smbSession;
}

SMB 入站通道介面卡

為了在本地下載 SMB 檔案,提供了 SmbInboundFileSynchronizingMessageSource。它是 AbstractInboundFileSynchronizingMessageSource 的簡單擴充套件,需要注入 SmbInboundFileSynchronizer。對於過濾遠端檔案,您仍然可以使用任何現有的 FileListFilter 實現,但也提供了特定的 SmbRegexPatternFileListFilterSmbSimplePatternFileListFilter

@Bean
public SmbInboundFileSynchronizer smbInboundFileSynchronizer() {
    SmbInboundFileSynchronizer fileSynchronizer =
        new SmbInboundFileSynchronizer(smbSessionFactory());
    fileSynchronizer.setFilter(compositeFileListFilter());
    fileSynchronizer.setRemoteDirectory("mySharedDirectoryPath");
    fileSynchronizer.setDeleteRemoteFiles(true);
    return fileSynchronizer;
}

@Bean
public CompositeFileListFilter<SmbFile> compositeFileListFilter() {
    CompositeFileListFilter<SmbFile> filters = new CompositeFileListFilter<>();
    filters.addFilter(new SmbRegexPatternFileListFilter("^(?i).+((\\.txt))$"));
    return filters;
}

@Bean
public MessageChannel smbFileInputChannel() {
    return new DirectChannel();
}

@Bean
@InboundChannelAdapter(value = "smbFileInputChannel",
                       poller = @Poller(fixedDelay = "2000"))
public MessageSource<File> smbMessageSource() {
    SmbInboundFileSynchronizingMessageSource messageSource =
        new SmbInboundFileSynchronizingMessageSource(smbInboundFileSynchronizer());
    messageSource.setLocalDirectory(new File("myLocalDirectoryPath"));
    messageSource.setAutoCreateLocalDirectory(true);
    return messageSource;
}

對於 XML 配置,提供了 <int-smb:inbound-channel-adapter> 元件。

從版本 6.2 開始,您可以使用 SmbLastModifiedFileListFilter 基於檔案最後修改時間策略過濾 SMB 檔案。此過濾器可以配置一個 age 屬性,以便只有比該值舊的檔案才能透過過濾器。預設 age 為 60 秒,但您應該選擇一個足夠大的 age 值,以避免過早選取檔案(例如,由於網路故障)。有關更多資訊,請查閱其 Javadoc。

使用 Java DSL 配置

以下 Spring Boot 應用程式示例展示瞭如何使用 Java DSL 配置入站介面卡:

@SpringBootApplication
public class SmbJavaApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(SmbJavaApplication.class)
            .web(false)
            .run(args);
    }

    @Bean
    public SmbSessionFactory smbSessionFactory() {
        SmbSessionFactory smbSession = new SmbSessionFactory();
        smbSession.setHost("myHost");
        smbSession.setPort(445);
        smbSession.setDomain("myDomain");
        smbSession.setUsername("myUser");
        smbSession.setPassword("myPassword");
        smbSession.setShareAndDir("myShareAndDir");
        smbSession.setSmbMinVersion(DialectVersion.SMB210);
        smbSession.setSmbMaxVersion(DialectVersion.SMB311);
        return smbSession;
    }

    @Bean
    public IntegrationFlow smbInboundFlow() {
        return IntegrationFlow
            .from(Smb.inboundAdapter(smbSessionFactory())
                    .preserveTimestamp(true)
                    .remoteDirectory("smbSource")
                    .regexFilter(".*\\.txt$")
                    .localFilename(f -> f.toUpperCase() + ".a")
                    .localDirectory(new File("d:\\smb_files")),
                        e -> e.id("smbInboundAdapter")
                    .autoStartup(true)
                    .poller(Pollers.fixedDelay(5000)))
            .handle(m -> System.out.println(m.getPayload()))
            .get();
    }
}

SMB 流式入站通道介面卡

此介面卡生成載荷型別為 InputStream 的訊息,允許無需寫入本地檔案系統即可獲取檔案。由於會話保持開啟狀態,消費應用程式負責在檔案被消費後關閉會話。會話在 closeableResource 頭 (IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE) 中提供。標準框架元件,例如 FileSplitterStreamTransformer,會自動關閉會話。有關這些元件的更多資訊,請參閱 檔案拆分器流轉換器。以下示例展示瞭如何配置 inbound-streaming-channel-adapter

<int-smb:inbound-streaming-channel-adapter id="smbInbound"
            channel="smbChannel"
            session-factory="sessionFactory"
            filename-pattern="*.txt"
            filename-regex=".*\.txt"
            filter="filter"
            filter-expression="@myFilterBean.check(#root)"
            remote-file-separator="/"
            comparator="comparator"
            max-fetch-size="1"
            remote-directory-expression="'foo/bar'">
        <int:poller fixed-rate="1000" />
</int-smb:inbound-streaming-channel-adapter>

只能使用 filename-patternfilename-regexfilterfilter-expression 中的一個。

SmbStreamingMessageSource 介面卡使用基於記憶體 SimpleMetadataStoreSmbPersistentAcceptOnceFileListFilter 來防止遠端檔案重複。預設情況下,此過濾器也應用於檔名模式(或正則表示式)。如果您需要允許重複,可以使用 AcceptAllFileListFilter。任何其他用例都可以透過 CompositeFileListFilter(或 ChainFileListFilter)處理。Java 配置(文件稍後)展示了一種在處理後刪除遠端檔案以避免重複的技術。

有關 SmbPersistentAcceptOnceFileListFilter 及其使用方法的更多資訊,請參閱 遠端持久化檔案列表過濾器

當需要獲取檔案時,使用 max-fetch-size 屬性限制每次輪詢獲取的檔案數量。在叢集環境中執行時,將其設定為 1 並使用持久化過濾器。有關更多資訊,請參閱 入站通道介面卡:控制遠端檔案獲取

介面卡將遠端目錄和檔名分別放入 FileHeaders.REMOTE_DIRECTORYFileHeaders.REMOTE_FILE 頭中。FileHeaders.REMOTE_FILE_INFO 頭提供額外的遠端檔案資訊(預設為 JSON 表示)。如果您將 SmbStreamingMessageSourcefileInfoJson 屬性設定為 false,則頭包含一個 SmbFileInfo 物件。

使用 Java 配置

以下 Spring Boot 應用程式示例展示瞭如何使用 Java 配置入站介面卡:

@SpringBootApplication
public class SmbJavaApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(SmbJavaApplication.class)
            .web(false)
            .run(args);
    }

    @Bean
    @InboundChannelAdapter(channel = "stream")
    public MessageSource<InputStream> smbMessageSource() {
        SmbStreamingMessageSource messageSource = new SmbStreamingMessageSource(template());
        messageSource.setRemoteDirectory("smbSource/");
        messageSource.setFilter(new AcceptAllFileListFilter<>());
        messageSource.setMaxFetchSize(1);
        return messageSource;
    }

    @Bean
    @Transformer(inputChannel = "stream", outputChannel = "data")
    public org.springframework.integration.transformer.Transformer transformer() {
        return new StreamTransformer("UTF-8");
    }

    @Bean
    public SmbRemoteFileTemplate template() {
        return new SmbRemoteFileTemplate(smbSessionFactory());
    }

    @ServiceActivator(inputChannel = "data", adviceChain = "after")
    @Bean
    public MessageHandler handle() {
        return System.out::println;
    }

    @Bean
    public ExpressionEvaluatingRequestHandlerAdvice after() {
        ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice();
        advice.setOnSuccessExpression(
                "@template.remove(headers['file_remoteDirectory'] + headers['file_remoteFile'])");
        advice.setPropagateEvaluationFailures(true);
        return advice;
    }

}

請注意,在此示例中,轉換器下游的訊息處理器包含一個 advice,該 advice 在處理後刪除遠端檔案。

入站通道介面卡:控制遠端檔案獲取

配置入站通道介面卡時,有兩個屬性需要考慮。與其他輪詢器一樣,max-messages-per-poll 可用於限制每次輪詢發出的訊息數量(如果準備就緒的訊息數量超過配置值)。max-fetch-size 可以限制一次從遠端伺服器檢索的檔案數量。

以下場景假定起始狀態是本地目錄為空:

  • max-messages-per-poll=2max-fetch-size=1:介面卡獲取一個檔案,發出,然後獲取下一個檔案,發出,然後休眠直到下一次輪詢。

  • max-messages-per-poll=2max-fetch-size=2:介面卡獲取這兩個檔案,然後發出每個檔案。

  • max-messages-per-poll=2max-fetch-size=4:介面卡最多獲取四個檔案(如果可用),併發出前兩個(如果至少有兩個)。接下來的兩個檔案將在下一次輪詢時發出。

  • max-messages-per-poll=2max-fetch-size 未指定:介面卡獲取所有遠端檔案,併發出前兩個(如果至少有兩個)。後續檔案在隨後的輪詢中發出(每次兩個)。當所有檔案都消費完畢後,會再次嘗試遠端獲取,以獲取任何新檔案。

當部署應用程式的多個例項時,我們建議使用較小的 max-fetch-size,以避免一個例項“搶佔”所有檔案而使其他例項飢餓。

max-fetch-size 的另一種用途是,如果您想停止獲取遠端檔案但繼續處理已獲取的檔案。透過在 MessageSource 上設定 maxFetchSize 屬性(透過程式設計、JMX 或 控制匯流排)可以有效阻止介面卡獲取更多檔案,但允許輪詢器繼續為之前已獲取的檔案發出訊息。如果屬性更改時輪詢器處於活動狀態,則更改將在下一次輪詢時生效。

可以為同步器提供一個 Comparator<SmbFile>。當使用 maxFetchSize 限制獲取的檔案數量時,這非常有用。

SMB 出站通道介面卡

為了將檔案寫入 SMB 共享,對於 XML <int-smb:outbound-channel-adapter> 元件,我們使用 SmbMessageHandler。在 Java 配置的情況下,需要為 SmbMessageHandler 提供 SmbSessionFactory(或 SmbRemoteFileTemplate)。

@Bean
@ServiceActivator(inputChannel = "storeToSmbShare")
public MessageHandler smbMessageHandler(SmbSessionFactory smbSessionFactory) {
    SmbMessageHandler handler = new SmbMessageHandler(smbSessionFactory);
    handler.setRemoteDirectoryExpression(
        new LiteralExpression("remote-target-dir"));
    handler.setFileNameGenerator(m ->
        m.getHeaders().get(FileHeaders.FILENAME, String.class) + ".test");
    handler.setAutoCreateDirectory(true);
    return handler;
}

使用 Java DSL 配置

以下 Spring Boot 應用程式示例展示瞭如何使用 Java DSL 配置出站介面卡:

@SpringBootApplication
@IntegrationComponentScan
public class SmbJavaApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context =
            new SpringApplicationBuilder(SmbJavaApplication.class)
                .web(false)
                .run(args);
        MyGateway gateway = context.getBean(MyGateway.class);
        gateway.sendToSmb(new File("/foo/bar.txt"));
    }

    @Bean
    public SmbSessionFactory smbSessionFactory() {
        SmbSessionFactory smbSession = new SmbSessionFactory();
        smbSession.setHost("myHost");
        smbSession.setPort(445);
        smbSession.setDomain("myDomain");
        smbSession.setUsername("myUser");
        smbSession.setPassword("myPassword");
        smbSession.setShareAndDir("myShareAndDir");
        smbSession.setSmbMinVersion(DialectVersion.SMB210);
        smbSession.setSmbMaxVersion(DialectVersion.SMB311);
        return smbSession;
    }

    @Bean
    public IntegrationFlow smbOutboundFlow() {
        return IntegrationFlow.from("toSmbChannel")
                .handle(Smb.outboundAdapter(smbSessionFactory(), FileExistsMode.REPLACE)
                        .useTemporaryFileName(false)
                        .fileNameExpression("headers['" + FileHeaders.FILENAME + "']")
                        .remoteDirectory("smbTarget")
                ).get();
    }

    @MessagingGateway
    public interface MyGateway {

         @Gateway(requestChannel = "toSmbChannel")
         void sendToSmb(File file);
    }

}

SMB 出站閘道器

SMB 出站閘道器提供一組有限的命令與遠端 SMB 伺服器互動。支援的命令有:

  • 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 物件可以做到這一點。

使用 nlst 命令

nlst 列出遠端檔名,僅支援一個選項:

  • -f: 不對列表進行排序。

nlst 操作產生的訊息載荷是一個檔名列表。

file_remoteDirectory 頭包含 nlst 命令作用的遠端目錄。

使用 get 命令

get 檢索遠端檔案,支援以下選項:

  • -P: 保留遠端檔案的時間戳。

  • -stream: 將遠端檔案檢索為流。

  • -D: 成功傳輸後刪除遠端檔案。如果傳輸被忽略(因為 FileExistsModeIGNORE 且本地檔案已存在),則不刪除遠端檔案。

file_remoteDirectory 頭包含遠端目錄,file_remoteFile 頭包含檔名。

get 操作產生的訊息載荷是一個 File 物件,表示檢索到的檔案。如果使用 -stream 選項,載荷是一個 InputStream 而不是 File。對於文字檔案,一個常見的用例是將此操作與 檔案拆分器流轉換器 結合使用。以流方式消費遠端檔案時,您負責在消費流後關閉 Session。為方便起見,SessioncloseableResource 頭中提供,IntegrationMessageHeaderAccessor 提供便捷方法:

Closeable closeable = new IntegrationMessageHeaderAccessor(message).getCloseableResource();
if (closeable != null) {
    closeable.close();
}

框架元件,例如 檔案拆分器流轉換器,在資料傳輸後會自動關閉會話。

以下示例展示瞭如何以流方式消費檔案:

<int-smb:outbound-gateway session-factory="smbSessionFactory"
                            request-channel="inboundGetStream"
                            command="get"
                            command-options="-stream"
                            expression="payload"
                            remote-directory="smbTarget"
                            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: 成功傳輸後刪除每個遠端檔案。如果傳輸被忽略(因為 FileExistsModeIGNORE 且本地檔案已存在),則不刪除遠端檔案。

mget 操作產生的訊息載荷是一個 List<File> 物件(即 File 物件列表,每個物件代表一個檢索到的檔案)。

如果 FileExistsModeIGNORE,則輸出訊息的載荷不再包含因檔案已存在而被忽略的檔案。之前,陣列包含所有檔案,包括已存在的檔案。

您用於確定遠端路徑的表示式應生成以 結尾的結果,例如 myfiles/ 獲取 myfiles 下的整個樹。

您可以將遞迴 MGETFileExistsMode.REPLACE_IF_MODIFIED 模式結合使用,以定期在本地同步整個遠端目錄樹。此模式將本地檔案的最後修改時間戳設定為遠端檔案的時間戳,無論是否使用 -P(保留時間戳)選項。

使用遞迴 (-R) 的注意事項:

模式將被忽略,並假定為 *。預設情況下,檢索整個遠端樹。但是,您可以透過提供 FileListFilter 來過濾樹中的檔案。您也可以透過這種方式過濾樹中的目錄。可以透過引用或透過 filename-patternfilename-regex 屬性提供 FileListFilter。例如,filename-regex="(subDir|.*1.txt)" 將檢索遠端目錄和子目錄 subDir 中所有以 1.txt 結尾的檔案。但是,我們將在本註釋之後描述一種替代方法。

如果您過濾了子目錄,則不會對該子目錄執行額外的遍歷。

不允許使用 -dirs 選項(遞迴 mget 使用遞迴 ls 獲取目錄樹,並且目錄本身不能包含在列表中)。

通常,您會在 local-directory-expression 中使用 #remoteDirectory 變數,以便在本地保留遠端目錄結構。

持久化檔案列表過濾器現在有一個布林屬性 forRecursion。將此屬性設定為 true,也會設定 alwaysAcceptDirectories,這意味著出站閘道器上的遞迴操作(lsmget)現在每次都會始終遍歷完整的目錄樹。這是為了解決目錄樹深處變更未被檢測到的問題。此外,forRecursion=true 會將檔案的完整路徑用作元資料儲存鍵;這解決了如果同一名稱的檔案在不同目錄中多次出現時過濾器無法正常工作的問題。重要:這意味著在持久化元資料儲存中,頂級目錄下的檔案將無法找到現有鍵。因此,此屬性預設為 false;這可能會在未來的版本中更改。

您可以配置 SmbSimplePatternFileListFilterSmbRegexPatternFileListFilter 透過將 alwaysAcceptDirectorties 設定為 true 來始終允許目錄透過。這樣做允許對簡單模式進行遞迴,如下例所示:

<bean id="starDotTxtFilter"
            class="org.springframework.integration.smb.filters.SmbSimplePatternFileListFilter">
    <constructor-arg value="*.txt" />
    <property name="alwaysAcceptDirectories" value="true" />
</bean>

<bean id="dotStarDotTxtFilter"
            class="org.springframework.integration.smb.filters.SmbRegexPatternFileListFilter">
    <constructor-arg value="^.*\.txt$" />
    <property name="alwaysAcceptDirectories" value="true" />
</bean>

您可以透過閘道器上的 filter 屬性提供這些過濾器之一。

使用 put 命令

put 將檔案傳送到遠端伺服器。訊息的載荷可以是 java.io.Filebyte[]String。使用 remote-filename-generator(或表示式)命名遠端檔案。其他可用屬性包括 remote-directorytemporary-remote-directory 及其對應的 *-expressionuse-temporary-file-nameauto-create-directory。有關更多資訊,請參閱模式文件

put 操作產生的訊息載荷是一個 String,其中包含檔案傳輸後在伺服器上的完整路徑。

使用 mput 命令

mput 將多個檔案傳送到伺服器,支援以下選項:

  • -R: 遞迴 - 傳送目錄和子目錄中所有(可能已過濾的)檔案。

訊息載荷必須是一個 java.io.File(或 String),代表一個本地目錄。也支援 FileString 的集合。

支援與 put 命令 相同的屬性。此外,您可以使用 mput-patternmput-regexmput-filtermput-filter-expression 中的一個來過濾本地目錄中的檔案。只要子目錄本身透過過濾器,該過濾器就支援遞迴。未透過過濾器的子目錄將不會被遞迴。

mput 操作產生的訊息載荷是一個 List<String> 物件(即傳輸後生成的遠端檔案路徑列表)。

使用 rm 命令

rm 命令沒有選項。

如果刪除操作成功,則生成的訊息載荷為 Boolean.TRUE。否則,訊息載荷為 Boolean.FALSEfile_remoteDirectory 頭包含遠端目錄,file_remoteFile 頭包含檔名。

使用 mv 命令

mv 命令沒有選項。

expression 屬性定義“源”路徑,rename-expression 屬性定義“目標”路徑。預設情況下,rename-expressionheaders['file_renameTo']。此表示式不得求值為 null 或空 String。如果需要,會建立所需的遠端目錄。結果訊息的載荷是 Boolean.TRUEfile_remoteDirectory 頭包含原始遠端目錄,file_remoteFile 頭包含檔名。file_renameTo 頭包含新路徑。

remoteDirectoryExpression 可用於 mv 命令,以方便使用。如果“源”檔案不是完整的 檔案路徑,則 remoteDirectoryExpression 的結果將用作遠端目錄。這同樣適用於“目標”檔案,例如,如果任務只是重新命名某個目錄中的遠端檔案。

附加命令資訊

getmget 命令支援 local-filename-generator-expression 屬性。它定義一個 SpEL 表示式,用於在傳輸期間生成本地檔案的名稱。求值上下文的根物件是請求訊息。remoteFileName 變數也可用。這對於 mget 特別有用(例如:local-filename-generator-expression="#remoteFileName.toUpperCase() + headers.foo")。

getmget 命令支援 local-directory-expression 屬性。它定義一個 SpEL 表示式,用於在傳輸期間生成本地目錄的名稱。求值上下文的根物件是請求訊息。remoteDirectory 變數也可用。這對於 mget 特別有用(例如:local-directory-expression="'/tmp/local/' + #remoteDirectory.toUpperCase() + headers.myheader")。此屬性與 local-directory 屬性互斥。

對於所有命令,閘道器的 'expression' 屬性包含命令作用的路徑。對於 mget 命令,表示式可能求值為 ,表示檢索所有檔案,somedirectory/ 等以 * 結尾的值。

以下示例顯示了一個配置為 ls 命令的閘道器:

<int-smb:outbound-gateway id="gateway1"
        session-factory="smbSessionFactory"
        request-channel="inbound1"
        command="ls"
        command-options="-1"
        expression="payload"
        reply-channel="toSplitter"/>

傳送到 toSplitter 通道的訊息的載荷是一個 String 物件列表,每個物件都包含一個檔案的名稱。如果您省略 command-options="-1",則載荷將是 FileInfo 物件列表。您可以將選項作為空格分隔列表提供(例如,command-options="-1 -dirs -links")。

GETMGETPUTMPUT 命令支援一個 FileExistsMode 屬性(使用名稱空間支援時是 mode)。這會影響當本地檔案存在(GETMGET)或遠端檔案存在(PUTMPUT)時的行為。支援的模式有 REPLACEAPPENDFAILIGNORE。為了向後相容,PUTMPUT 操作的預設模式是 REPLACE。對於 GETMGET 操作,預設模式是 FAIL

使用 Java 配置進行配置

以下 Spring Boot 應用展示瞭如何使用 Java 配置來配置出站閘道器的示例

@SpringBootApplication
public class SmbJavaApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(SmbJavaApplication.class)
            .web(false)
            .run(args);
    }

    @Bean
    public SmbSessionFactory smbSessionFactory() {
        SmbSessionFactory smbSession = new SmbSessionFactory();
        smbSession.setHost("myHost");
        smbSession.setPort(445);
        smbSession.setDomain("myDomain");
        smbSession.setUsername("myUser");
        smbSession.setPassword("myPassword");
        smbSession.setShareAndDir("myShareAndDir");
        smbSession.setSmbMinVersion(DialectVersion.SMB210);
        smbSession.setSmbMaxVersion(DialectVersion.SMB311);
        return smbSession;
    }

    @Bean
    @ServiceActivator(inputChannel = "smbChannel")
    public MessageHandler handler() {
        SmbOutboundGateway smbOutboundGateway =
            new SmbOutboundGateway(smbSessionFactory(), "'my_remote_dir/'");
        smbOutboundGateway.setOutputChannelName("replyChannel");
        return smbOutboundGateway;
    }

}

使用 Java DSL 進行配置

以下 Spring Boot 應用展示瞭如何使用 Java DSL 來配置出站閘道器的示例

@SpringBootApplication
public class SmbJavaApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(SmbJavaApplication.class)
            .web(false)
            .run(args);
    }

    @Bean
    public SmbSessionFactory smbSessionFactory() {
        SmbSessionFactory smbSession = new SmbSessionFactory();
        smbSession.setHost("myHost");
        smbSession.setPort(445);
        smbSession.setDomain("myDomain");
        smbSession.setUsername("myUser");
        smbSession.setPassword("myPassword");
        smbSession.setShareAndDir("myShareAndDir");
        smbSession.setSmbMinVersion(DialectVersion.SMB210);
        smbSession.setSmbMaxVersion(DialectVersion.SMB311);
        return smbSession;
    }

    @Bean
    public SmbOutboundGatewaySpec smbOutboundGateway() {
        return Smb.outboundGateway(smbSessionFactory(),
            AbstractRemoteFileOutboundGateway.Command.MGET, "payload")
            .options(AbstractRemoteFileOutboundGateway.Option.RECURSIVE)
            .regexFileNameFilter("(subSmbSource|.*.txt)")
            .localDirectoryExpression("'localDirectory/' + #remoteDirectory")
            .localFilenameExpression("#remoteFileName.replaceFirst('smbSource', 'localTarget')");
    }

    @Bean
    public IntegrationFlow smbFlow(AbstractRemoteFileOutboundGateway<SmbFile> smbOutboundGateway) {
        return f -> f
            .handle(smbOutboundGateway)
            .channel(c -> c.queue("remoteFileOutputChannel"));
    }

}

出站閘道器部分成功(mgetmput

當對多個檔案執行操作(使用 mgetmput)時,在傳輸一個或多個檔案後可能會發生異常。在這種情況下,會丟擲 PartialSuccessException。除了通常的 MessagingException 屬性(failedMessagecause)之外,此異常還有兩個額外的屬性

  • partialResults:成功的傳輸結果。

  • derivedInput:根據請求訊息生成的檔案列表(例如 mput 要傳輸的本地檔案)。

這些屬性使您能夠確定哪些檔案成功傳輸,哪些檔案未能成功傳輸。

在遞迴的 mput 情況下,PartialSuccessException 可能包含巢狀的 PartialSuccessException 例項。

考慮以下目錄結構

root/
|- file1.txt
|- subdir/
   | - file2.txt
   | - file3.txt
|- zoo.txt

如果異常發生在 file3.txt 上,則閘道器丟擲的 PartialSuccessExceptionderivedInputfile1.txtsubdirzoo.txtpartialResultsfile1.txt。它的 cause 是另一個 PartialSuccessException,其 derivedInputfile2.txtfile3.txtpartialResultsfile2.txt

遠端檔案資訊

SmbStreamingMessageSourceSMB 流式入站通道介面卡)、SmbInboundFileSynchronizingMessageSourceSMB 入站通道介面卡)以及 SmbOutboundGatewaySMB 出站閘道器)的“讀取”命令在生成的訊息中提供關於遠端檔案的附加訊息頭

  • FileHeaders.REMOTE_HOST_PORT - 檔案傳輸操作期間遠端會話連線到的主機:埠 對;

  • FileHeaders.REMOTE_DIRECTORY - 執行操作的遠端目錄;

  • FileHeaders.REMOTE_FILE - 遠端檔名;僅適用於單個檔案操作。

由於 SmbInboundFileSynchronizingMessageSource 不直接針對遠端檔案生成訊息,而是使用本地副本,AbstractInboundFileSynchronizer 在同步操作期間以 URI 風格(protocol://host:port/remoteDirectory#remoteFileName)將遠端檔案的資訊儲存在 MetadataStore(可外部配置)中。當輪詢本地檔案時,SmbInboundFileSynchronizingMessageSource 會檢索此元資料。當刪除本地檔案時,建議移除其元資料條目。為此,AbstractInboundFileSynchronizer 提供了一個 removeRemoteFileMetadata() 回撥。此外,還有一個用於元資料鍵的 setMetadataStorePrefix()。當在這些元件之間共享同一個 MetadataStore 例項時,建議使此字首與基於 MetadataStoreFileListFilter 實現中使用的字首不同,以避免條目覆蓋,因為過濾器和 AbstractInboundFileSynchronizer 都使用相同的本地檔名作為元資料條目鍵。