|
此版本仍在开发中,尚未被视为稳定版。如需最新稳定版本,请使用 Spring Integration 7.0.4! |
SMB 支持
Spring Integration 提供了对 SMB 文件传输操作的支持。
服务器消息块 (SMB) 是一种简单的网络协议,允许您将文件传输到共享文件服务器。
您需要将以下依赖项包含到您的项目中:
-
Maven
-
Gradle
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-smb</artifactId>
<version>6.5.9-SNAPSHOT</version>
</dependency>
compile "org.springframework.integration:spring-integration-smb:6.5.9-SNAPSHOT"
概述
The Java CIFS 客户端库已被选为 CIFS/SMB 网络协议的 Java 实现。
其 SmbFile 抽象被简单地封装到 Spring Integration "远程文件" 基础结构中,如 SmbSession、SmbRemoteFileTemplate 等。
SMB Channel Adapters 和支持类的实现与现有的 (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 会话工厂:
The 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;
}
The SmbSessionFactory can be initialized with a custom jcifs.CIFSContext.
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 会话缓存
SmbSessionFactory 在每次请求 Session 时都会发起一个新连接。
在大多数情况下,这是不必要的,Session 可以被缓存。
为此,如前所述的 SmbSessionFactory 应该被包装在一个 CachingSessionFactory 实例中:
@Bean
public CachingSessionFactory cachingSessionFactory(SmbSessionFactory smbSessionFactory) {
cachingSessionFactory cachingSessionFactory = new CachingSessionFactory(smbSessionFactory, 10);
cachingSessionFactory.setSessionWaitTimeout(1000);
return cachingSessionFactory;
}
然后,其 Bean 可以被注入到下面描述的通道适配器中。
SMB 入站通道适配器
要本地下载 SMB 文件,提供了SmbInboundFileSynchronizingMessageSource。
它是AbstractInboundFileSynchronizingMessageSource的简单扩展,需要SmbInboundFileSynchronizer注入。
对于过滤远程文件,您仍然可以使用任何现有的FileListFilter实现,但也提供了特定的SmbRegexPatternFileListFilter和SmbSimplePatternFileListFilter。
@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 以获取更多信息。
相比之下,从版本 6.5 开始,引入了 SmbRecentFileListFilter 参数,用于仅接受不早于所提供的 age 的文件。
使用 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)提供。
标准框架组件(如 FileSplitter 和 StreamTransformer)会自动关闭会话。
有关这些组件的更多信息,请参阅 文件拆分器 和 流转换器。
以下示例展示了如何配置 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-pattern、filename-regex、filter 或 filter-expression 中的一个。
SmbStreamingMessageSource适配器基于内存中的SimpleMetadataStore防止远程文件出现重复(以SmbPersistentAcceptOnceFileListFilter为依据)。
默认情况下,此过滤器也会应用于文件名模式(或正则表达式)。
如果您需要允许重复,可以使用AcceptAllFileListFilter。
其他任何用例都可以通过CompositeFileListFilter(或ChainFileListFilter)来处理。
Java配置(本文档后文部分)展示了一种在处理后移除远程文件以避免重复的技术。
有关 SmbPersistentAcceptOnceFileListFilter 的更多信息及其用法,请参阅 远程持久文件列表过滤器。
使用 max-fetch-size 属性来限制在需要获取时每次轮询获取的文件数量。
将其设置为 1,并在集群环境中运行时使用持久化过滤器。
有关更多信息,请参阅 入站通道适配器:控制远程文件获取。
适配器将远程目录和文件名分别放入FileHeaders.REMOTE_DIRECTORY和FileHeaders.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,用于在处理完成后删除远程文件。
入站通道适配器:控制远程文件获取
在配置入站通道适配器时,您应该考虑两个属性。
max-messages-per-poll,与所有轮询器一样,可用于限制每次轮询发出的消息数量(如果准备就绪的消息超过配置值)。
max-fetch-size可限制一次从远程服务器检索的文件数量。
以下场景假设起始状态是一个空的本地目录:
-
max-messages-per-poll=2和max-fetch-size=1:适配器获取一个文件,发出它,获取下一个文件,发出它,然后进入休眠状态直到下一次轮询。 -
max-messages-per-poll=2andmax-fetch-size=2): 适配器会获取这两个文件,然后依次发出每一个。 -
max-messages-per-poll=2和max-fetch-size=4:适配器最多获取四个文件(如果可用),并输出前两个(如果至少有两个)。 接下来的两个文件将在下一次轮询时输出。 -
max-messages-per-poll=2和max-fetch-size未指定:适配器会获取所有远程文件,并输出前两个(如果至少有两个)。 后续的文件将在后续的轮询中输出(每次两个)。 当所有文件都被消费后,将再次尝试获取远程文件,以捕获任何新文件。
当您部署应用程序的多个实例时,我们建议设置一个较小的 max-fetch-size,以避免某个实例“抢占”所有文件而导致其他实例资源匮乏。 |
将 max-fetch-size 用于另一种用途:如果您希望停止获取远程文件,但继续处理已经获取的文件。
在 MessageSource 上设置 maxFetchSize 属性(通过编程方式、JMX 或使用 控制总线)可有效阻止适配器继续获取新文件,同时允许轮询器继续为之前已获取的文件发送消息。
如果在更改该属性时轮询器处于活动状态,则更改将在下一次轮询时生效。
同步器可以接受一个Comparator<SmbFile>值。
当限制使用maxFetchSize获取的文件数量时,这非常有用。
SMB 出站通道适配器
对于向 SMB 共享写入文件以及使用 XML <int-smb:outbound-channel-adapter> 组件,我们采用 SmbMessageHandler。
在 Java 配置情况下,需要提供 SmbSessionFactory(或 SmbRemoteFileTemplate)作为 SmbMessageHandler。
@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: 传输成功后删除远程文件。 如果传输被忽略,则不会删除远程文件,因为FileExistsMode为IGNORE且本地文件已存在。
The 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-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: 在传输成功后删除每个远程文件。 如果传输被忽略,则不会删除远程文件,因为FileExistsMode为IGNORE且本地文件已存在。
由 mget 操作产生的消息负载是一个 List<File> 对象(即一个包含 File 个对象的 List,每个对象代表一个检索到的文件)。
如果 FileExistsMode 为 IGNORE,则输出消息的有效载荷将不再包含因文件已存在而未获取的文件。
此前,数组包含所有文件,包括那些已存在的文件。 |
您使用的表达式决定了远程路径,它应产生一个以for example myfiles/获取完整树下的myfiles.
您可以使用递归MGET,结合FileExistsMode.REPLACE_IF_MODIFIED模式,定期将远程目录树同步到本地。
此模式会将本地文件的最后修改时间戳设置为远程文件的时间戳,无论是否启用-P(保留时间戳)选项。
|
关于使用递归的笔记 (
-R)该模式将被忽略,并假定为 如果过滤了子目录,则不会对该子目录执行额外的遍历。 不允许使用 通常,您会在 |
持久化文件列表过滤器现在有一个布尔属性 forRecursion。
将此属性设置为 true,也会设置 alwaysAcceptDirectories,这意味着对外部网关(ls 和 mget)的递归操作现在将每次遍历完整的目录树。
这是为了解决目录树深处更改未被检测到的问题。
此外,forRecursion=true 会导致使用文件的完整路径作为元数据存储键;这解决了如果同一名称的文件出现在不同目录中多次时过滤器无法正常工作的问题。
重要提示:这意味着持久化元数据存储中的现有键将无法在顶层目录下的文件中找到。
因此,该属性默认值为 false;此行为可能在未来的版本中发生变化。
您可以通过将alwaysAcceptDirectorties设置为true,来配置SmbSimplePatternFileListFilter和SmbRegexPatternFileListFilter以始终传递目录。
这样做允许对简单模式进行递归,如下例所示:
<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属性来提供这些过滤器之一。
另请参阅 出站网关部分成功 (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,其中包含文件在服务器上传输后的完整路径。
使用mput命令
mput 向服务器发送多个文件,并支持以下选项:
-
-R: 递归 — 发送目录及子目录中的所有文件(可能经过过滤)
消息负载必须是一个代表本地目录的 java.io.File(或 String)。
也支持集合类型的 File 或 String。
支持与 put 命令 相同的属性。
此外,您可以使用 mput-pattern、mput-regex、mput-filter 或 mput-filter-expression 之一来过滤本地目录中的文件。
该过滤器支持递归操作,只要子目录本身也通过过滤器即可继续递归。
未通过过滤器的子目录将不会被递归处理。
由 mput 操作生成的消息负载是一个 List<String> 对象(即,由传输产生的远程文件路径的 List)。
另请参阅 出站网关部分成功 (mget 和 mput)。
使用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头信息包含新路径。
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-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")。
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 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"));
}
}
出站网关部分成功 (mget和mput)
当对多个文件执行操作时(使用 mget 和 mput),在其中一个或多个文件传输完成后的一段时间内可能会发生异常。
此时会抛出 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 是另一个带有 derivedInput 的 file2.txt 和 file3.txt,以及 partialResults 的 file2.txt 的 PartialSuccessException。
远程文件信息
SmbStreamingMessageSource(SMB 流式传输入站通道适配器)、SmbInboundFileSynchronizingMessageSource(SMB 入站通道适配器)以及SmbOutboundGateway(SMB 出站网关)的"read"命令会在消息中提供额外的头部信息,用于生成有关远程文件的信息:
-
FileHeaders.REMOTE_HOST_PORT- 远程会话在文件传输操作期间连接到的主机:端口对; -
FileHeaders.REMOTE_DIRECTORY- 操作已执行的远程目录; -
FileHeaders.REMOTE_FILE- 远程文件名;仅适用于单个文件操作。
由于 SmbInboundFileSynchronizingMessageSource 不会针对远程文件生成消息,而是使用本地副本,因此在同步操作期间,AbstractInboundFileSynchronizer 会以 URI 样式(protocol://host:port/remoteDirectory#remoteFileName)将关于远程文件的信息存储在 MetadataStore(可外部配置)中。
当轮询本地文件时,该元数据由 SmbInboundFileSynchronizingMessageSource 检索。
删除本地文件时,建议移除其对应的元数据条目。
为此,AbstractInboundFileSynchronizer 提供了一个 removeRemoteFileMetadata() 回调。
此外,还有一个 setMetadataStorePrefix() 可用于元数据键中。
建议在同一个 MetadataStore 实例在这些组件间共享时,使此前缀与基于 MetadataStore 的 FileListFilter 实现中使用的前缀不同,以避免条目被覆盖,因为过滤器和 AbstractInboundFileSynchronizer 都使用相同的本地文件名作为元数据条目的键。