SMB 支援

Spring Integration 提供 SMB 檔案傳輸操作的支援。

伺服器訊息區塊 (SMB) 是一種簡單的網路協定,可讓您將檔案傳輸到共用檔案伺服器。

您需要將此相依性包含到您的專案中

  • Maven

  • Gradle

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

總覽

Java CIFS Client Library 已被選為 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 Session Factory

在設定 SMB 配接器之前,您必須設定 SMB session factory。您可以使用常規 Bean 定義來設定 SMB session factory,如下列範例所示

SmbSessionFactory 公開了使用 Min/Max 版本設定 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;
}

可以使用自訂 jcifs.CIFSContext 初始化 SmbSessionFactory

SMB 協定 Min/Max 版本的設定必須在您的 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 屬性設定此篩選器,以便僅有早於此值的文件才會通過篩選器。預設年齡為 60 秒,但您應選擇足夠大的年齡,以避免過早地選取檔案(例如,由於網路故障)。請查看其 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 串流輸入通道配接器

此配接器產生 payload 類型為 InputStream 的訊息,允許在不寫入本機檔案系統的情況下擷取檔案。由於 session 保持開啟,因此使用應用程式負責在檔案被使用後關閉 session。session 在 closeableResource 標頭 (IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE) 中提供。標準框架組件(例如 FileSplitterStreamTransformer)會自動關閉 session。請參閱 檔案分割器串流轉換器 以取得有關這些組件的更多資訊。以下範例顯示如何設定 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 防止遠端檔案重複。依預設,此篩選器也適用於檔案名稱模式(或 regex)。如果您需要允許重複,可以使用 AcceptAllFileListFilter。任何其他用例都可以由 CompositeFileListFilter (或 ChainFileListFilter) 處理。Java 設定(文件稍後部分)顯示了一種在處理後移除遠端檔案以避免重複的技術。

有關 SmbPersistentAcceptOnceFileListFilter 及其使用方式的更多資訊,請參閱 遠端持久性檔案清單篩選器

當需要擷取時,請使用 max-fetch-size 屬性來限制每次輪詢擷取的檔案數量。在叢集環境中執行時,將其設定為 1 並使用持久性篩選器。請參閱 輸入通道配接器:控制遠端檔案擷取 以取得更多資訊。

配接器將遠端目錄和檔案名稱分別放入 FileHeaders.REMOTE_DIRECTORYFileHeaders.REMOTE_FILE 標頭中。FileHeaders.REMOTE_FILE_INFO 標頭提供額外的遠端檔案資訊(依預設以 JSON 表示)。如果您將 SmbStreamingMessageSource 上的 fileInfoJson 屬性設定為 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,可在處理後移除遠端檔案。

輸入通道配接器:控制遠端檔案擷取

設定輸入通道配接器時,應考慮兩個屬性。與所有 poller 一樣,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=2 且未指定 max-fetch-size:配接器擷取所有遠端檔案,並發出前兩個檔案(如果至少有兩個檔案)。後續檔案在後續輪詢中發出(一次兩個)。當所有檔案都使用完畢後,會再次嘗試遠端擷取,以擷取任何新檔案。

當您部署應用程式的多個實例時,我們建議使用較小的 max-fetch-size,以避免一個實例「抓取」所有檔案並使其他實例處於飢餓狀態。

max-fetch-size 的另一個用途是,如果您想停止擷取遠端檔案,但繼續處理已擷取的檔案。在 MessageSource 上設定 maxFetchSize 屬性(以程式設計方式、使用 JMX 或使用 控制匯流排)有效地阻止配接器擷取更多檔案,但讓 poller 繼續發出先前已擷取檔案的訊息。如果 poller 在屬性變更時處於活動狀態,則變更會在下一次輪詢時生效。

可以為同步器提供 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 操作產生的訊息 payload 是檔案名稱清單或 FileInfo 物件清單(取決於您是否使用 -1 切換)。這些物件提供修改時間、權限和其他資訊。

ls 命令作用的遠端目錄在 file_remoteDirectory 標頭中提供。

當使用遞迴選項 (-R) 時,fileName 包含任何子目錄元素,並表示檔案的相對路徑(相對於遠端目錄)。如果您使用 -dirs 選項,則每個遞迴目錄也會作為清單中的元素傳回。在這種情況下,我們建議您不要使用 -1 選項,因為您將無法區分檔案和目錄,而當您使用 FileInfo 物件時可以做到這一點。

使用 nlst 命令

nlst 列出遠端檔案名稱,並且僅支援一個選項

  • -f:不對清單排序

nlst 操作產生的訊息 payload 是檔案名稱清單。

file_remoteDirectory 標頭保存 nlst 命令作用的遠端目錄。

使用 get 命令

get 擷取遠端檔案並支援以下選項

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

  • -stream:將遠端檔案擷取為串流。

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

file_remoteDirectory 標頭保存遠端目錄,而 file_remoteFile 標頭保存檔案名稱。

get 操作產生的訊息 payload 是一個 File 物件,表示擷取的檔案。如果您使用 -stream 選項,則 payload 是 InputStream 而不是 File。對於文字檔案,常見的用例是將此操作與 檔案分割器串流轉換器 結合使用。當以串流方式使用遠端檔案時,您有責任在使用完串流後關閉 Session。為了方便起見,SessioncloseableResource 標頭中提供,而 IntegrationMessageHeaderAccessor 提供方便的方法

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

框架組件(例如 檔案分割器串流轉換器)會在資料傳輸後自動關閉 session。

以下範例顯示如何以串流方式使用檔案

<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 操作產生的訊息 payload 是一個 List<File> 物件(即 File 物件的 List,每個物件代表一個擷取的檔案)。

如果 FileExistsModeIGNORE,則輸出訊息的 payload 不再包含由於檔案已存在而未擷取的檔案。先前,陣列包含所有檔案,包括已存在的檔案。

您用來決定遠端路徑的運算式應產生以 結尾的結果,例如 myfiles/ 擷取 myfiles 下的完整樹狀結構。

您可以使用遞迴 MGET,結合 FileExistsMode.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;這可能會在未來的版本中變更。

您可以透過將 alwaysAcceptDirectorties 設定為 true,將 SmbSimplePatternFileListFilterSmbRegexPatternFileListFilter 設定為始終傳遞目錄。這樣做允許簡單模式的遞迴,如下列範例所示

<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 將檔案傳送到遠端伺服器。訊息的 payload 可以是 java.io.Filebyte[]Stringremote-filename-generator(或運算式)用於命名遠端檔案。其他可用的屬性包括 remote-directorytemporary-remote-directory 及其 *-expression 對應項:use-temporary-file-nameauto-create-directory。請參閱 結構描述文件 以取得更多資訊。

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

使用 mput 命令

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

  • -R:遞迴 — 傳送目錄和子目錄中的所有檔案(可能已篩選)

訊息 payload 必須是 java.io.File(或 String),表示本機目錄。也支援 FileString 的集合。

支援與 put 命令 相同的屬性。此外,您可以使用 mput-patternmput-regexmput-filtermput-filter-expression 之一篩選本機目錄中的檔案。只要子目錄本身通過篩選器,篩選器就可以與遞迴一起使用。未通過篩選器的子目錄不會被遞迴。

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

使用 rm 命令

rm 命令沒有選項。

如果移除操作成功,則產生的訊息 payload 為 Boolean.TRUE。否則,訊息 payload 為 Boolean.FALSEfile_remoteDirectory 標頭保存遠端目錄,而 file_remoteFile 標頭保存檔案名稱。

使用 mv 命令

mv 命令沒有選項。

expression 屬性定義「來源」路徑,而 rename-expression 屬性定義「目標」路徑。依預設,rename-expressionheaders['file_renameTo']。此運算式不得評估為 null 或空 String。如有必要,將建立任何需要的遠端目錄。結果訊息的 payload 為 Boolean.TRUEfile_remoteDirectory 標頭保存原始遠端目錄,而 file_remoteFile 標頭保存檔案名稱。file_renameTo 標頭保存新路徑。

為了方便起見,可以在 mv 命令中使用 remoteDirectoryExpression。如果「來源」檔案不是完整檔案路徑,則 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 Configuration 進行配置

以下 Spring Boot 應用程式展示了如何使用 Java Configuration 配置出站閘道的範例

@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.txt,而 partialResultsfile1.txt。其 cause 是另一個 PartialSuccessException,其 derivedInputfile2.txtfile3.txt,而 partialResultsfile2.txt

遠端檔案資訊

SmbStreamingMessageSource (SMB 串流入站通道配接器)、SmbInboundFileSynchronizingMessageSource (SMB 入站通道配接器) 和 SmbOutboundGateway 的 "read" 命令 (SMB 出站閘道) 在訊息中提供額外的標頭,以產生關於遠端檔案的資訊

  • FileHeaders.REMOTE_HOST_PORT - 遠端連線階段作業已連線的主機:埠對,用於檔案傳輸操作;

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

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

由於 SmbInboundFileSynchronizingMessageSource 不會針對遠端檔案產生訊息,而是使用本機副本,因此 AbstractInboundFileSynchronizer 會將關於遠端檔案的資訊儲存在 MetadataStore (可以外部配置) 中,格式為 URI 樣式 (protocol://host:port/remoteDirectory#remoteFileName),在同步操作期間。當輪詢本機檔案時,SmbInboundFileSynchronizingMessageSource 會檢索此元數據。當本機檔案被刪除時,建議移除其元數據條目。AbstractInboundFileSynchronizer 為此目的提供了 removeRemoteFileMetadata() 回呼。此外,還有一個 setMetadataStorePrefix() 可用於元數據金鑰中。建議此前綴與基於 MetadataStoreFileListFilter 實作中使用的前綴不同,當相同的 MetadataStore 實例在這些組件之間共享時,以避免條目覆蓋,因為過濾器和 AbstractInboundFileSynchronizer 都使用相同的本機檔案名稱作為元數據條目金鑰。