FTP/FTPS 适配器(二)_递归

使用命令​​mget​

​mget​​基于模式检索多个远程文件,并支持以下选项:

  • ​-P​​:保留远程文件的时间戳。
  • ​-R​​:以递归方式检索整个目录树。
  • ​-x​​:如果没有文件与模式匹配,则引发异常(否则返回空列表)。
  • ​-D​​:成功传输后删除每个远程文件。 如果忽略传输,则不会删除远程文件,因为 和本地文件已存在。FileExistsModeIGNORE

由操作生成的消息负载是一个对象(即,一个对象,每个对象表示一个检索到的文件)。​​mget​​​​List<File>​​​​List​​​​File​

从版本 5.0 开始,如果 is ,则输出消息的有效负载不再包含由于文件已存在而未获取的文件。 以前,该列表包含所有文件,包括已存在的文件。​​FileExistsMode​​​​IGNORE​

用于确定远程路径的表达式应产生以 - 例如 ​​somedir/​​ 将在 .​​somedir​

从版本 5.0 开始,递归 ,结合新模式,可用于定期在本地同步整个远程目录树。 此模式将本地文件的上次修改时间戳替换为远程时间戳,而不考虑(保留时间戳)选项。​​mget​​​​FileExistsMode.REPLACE_IF_MODIFIED​​​​-P​

使用递归 (​​-R​​)


该模式将被忽略,并被假定。 默认情况下,将检索整个远程树。 但是,可以通过提供 . 树中的目录也可以通过这种方式进行过滤。 A 可以通过引用、按 或按属性提供。 例如, 检索远程目录和子目录中以 结尾的所有文件。 但是,下一个示例显示了版本 5.0 提供的替代方法。​​*​​​​FileListFilter​​​​FileListFilter​​​​filename-pattern​​​​filename-regex​​​​filename-regex="(subDir|.*1.txt)"​​​​1.txt​​​​subDir​



如果筛选了子目录,则不会对该子目录执行其他遍历。



不允许使用该选项(递归使用递归获取目录树,因此目录本身不能包含在列表中)。​​-dirs​​​​mget​​​​ls​



通常,您将在 中使用该变量,以便在本地保留远程目录结构。​​#remoteDirectory​​​​local-directory-expression​


持久文件列表筛选器现在具有布尔属性。 将此属性设置为 也会设置 ,这意味着出站网关 ( 和 ) 上的递归操作现在每次将始终遍历整个目录树。 这是为了解决未检测到目录树深处的更改的问题。 此外,导致将文件的完整路径用作元数据存储键;这解决了如果具有相同名称的文件在不同目录中多次出现,过滤器无法正常工作的问题。 重要说明:这意味着对于顶级目录下的文件,将找不到持久元数据存储中的现有密钥。 因此,默认情况下,该属性是;这可能会在将来的版本中更改。​​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 引入了该属性,该属性在上传后更改远程文件权限。 您可以使用传统的 Unix 八进制格式(例如,仅允许文件所有者读写)。 使用 java 配置适配器时,可以使用 。 仅当 FTP 服务器支持子命令时才适用。​​chmod​​​​600​​​​setChmod(0600)​​​​SITE CHMOD​

使用命令​​mput​

将多个文件发送到服务器,并且仅支持一个选项:​​mput​

  • ​-R​​:递归的。 发送目录及其子目录中的所有文件(可能已过滤)。

消息负载必须是表示本地目录的(或)。 从版本 5.1 开始,还支持 or 的集合。​​java.io.File​​​​String​​​​File​​​​String​

此命令支持与 put 命令相同的属性。 此外,可以使用 、、 或 之一过滤本地目录中的文件。 过滤器与递归一起工作,只要子目录本身通过过滤器。 未通过筛选器的子目录不会递归。​​mput-pattern​​​​mput-regex​​​​mput-filter​​​​mput-filter-expression​

操作生成的消息负载是一个对象(即传输产生的远程文件路径)。​​mput​​​​List<String>​​​​List​

另请参阅出站网关部分成功(mget 和 mput)。

版本 5.2 引入了该属性,该属性允许您在上传后更改远程文件权限。 您可以使用传统的 Unix 八进制格式(例如,仅允许文件所有者读写)。 使用 Java 配置适配器时,可以使用 或 。 仅当 FTP 服务器支持子命令时才适用。​​chmod​​​​600​​​​setChmodOctal("600")​​​​setChmod(0600)​​​​SITE CHMOD​

使用命令​​rm​

该命令将删除文件。​​rm​

该命令没有选项。​​rm​

操作产生的消息负载是删除成功还是成功。 标头提供远程目录,标头提供文件名。​​rm​​​​Boolean.TRUE​​​​Boolean.FALSE​​​​file_remoteDirectory​​​​file_remoteFile​

使用命令​​mv​

该命令移动文件。​​mv​

该命令没有选项。​​mv​

该属性定义“发件人”路径,属性定义“to”路径。 默认情况下,为 . 此表达式的计算结果不得为 null 或空 。 如有必要,将创建任何必要的远程目录。 结果消息的有效负载为 。 标头提供原始远程目录,标头提供文件名。 新路径位于标头中。​​expression​​​​rename-expression​​​​rename-expression​​​​headers['file_renameTo']​​​​String​​​​Boolean.TRUE​​​​file_remoteDirectory​​​​file_remoteFile​​​​file_renameTo​

从版本 5.5.6 开始,为方便起见,可以在命令中使用 。 如果“from”文件不是完整的文件路径,则 的结果将用作远程目录。 这同样适用于“to”文件,例如,如果任务只是重命名某个目录中的远程文件。​​remoteDirectoryExpression​​​​mv​​​​remoteDirectoryExpression​

有关 FTP 出站网关命令的其他信息

和命令支持该属性。 它定义了一个 SpEL 表达式,用于在传输过程中生成本地文件的名称。 评估上下文的根对象是请求消息。 该变量对 特别有用,也可用 — 例如,。​​get​​​​mget​​​​local-filename-generator-expression​​​​remoteFileName​​​​mget​​​​local-filename-generator-expression="#remoteFileName.toUpperCase() + headers.something"​

和命令支持该属性。 它定义了一个 SpEL 表达式,用于在传输过程中生成本地目录的名称。 评估上下文的根对象是请求消息但是。 该变量对 特别有用,也可用 — 例如: 。 此属性与该属性互斥。​​get​​​​mget​​​​local-directory-expression​​​​remoteDirectory​​​​mget​​​​local-directory-expression="'/tmp/local/' + #remoteDirectory.toUpperCase() + headers.something"​​​​local-directory​

对于所有命令,网关的“expression”属性提供命令操作的路径。 对于该命令,表达式的计算结果可能为 '',表示检索所有文件或 'somedirectory/',依此类推。​​mget​

以下示例显示了为命令配置的网关:​​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 开始,( 在 XML 中)上提供了 ( 在 XML 中) 选项。 它允许您在运行时更改客户端工作目录。 根据请求消息计算表达式。 每次网关操作后都会恢复以前的工作目录。​​setWorkingDirExpression()​​​​working-dir-expression​​​​FtpOutboundGateway​​​​<int-ftp:outbound-gateway>​

使用 Java 配置进行配置

以下 Spring 引导应用程序显示了如何使用 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 引导应用程序显示了如何使用 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​​)

对多个文件执行操作(通过使用 和)时,在传输一个或多个文件后的一段时间内可能会发生异常。 在这种情况下(从版本 4.2 开始),抛出 a。 除了常用属性 ( 和 ) 之外,此异常还有两个附加属性:​​mget​​​​mput​​​​PartialSuccessException​​​​MessagingException​​​​failedMessage​​​​cause​

  • ​partialResults​​:传输成功结果。
  • ​derivedInput​​:从请求消息生成的文件列表(例如,要传输的本地文件)。mput

这些属性使您可以确定哪些文件已成功传输,哪些文件未成功传输。

在递归的情况下,可能有嵌套的出现。​​mput​​​​PartialSuccessException​​​​PartialSuccessException​

请考虑以下目录结构:

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

如果异常发生在 上,则网关抛出的 具有 、 和 的 。 它是另一个 with of 和 and of .​​file3.txt​​​​PartialSuccessException​​​​derivedInput​​​​file1.txt​​​​subdir​​​​zoo.txt​​​​partialResults​​​​file1.txt​​​​cause​​​​PartialSuccessException​​​​derivedInput​​​​file2.txt​​​​file3.txt​​​​partialResults​​​​file2.txt​

FTP 会话缓存

从 Spring Integration 3.0 开始,默认情况下不再缓存会话。 端点不再支持该属性。 如果要缓存会话,则必须使用 (在下一个示例中所示)。​​cache-sessions​​​​CachingSessionFactory​

在 3.0 之前的版本中,会话默认自动缓存。 属性可用于禁用自动缓存,但该解决方案不提供配置其他会话缓存属性的方法。 例如,您无法限制创建的会话数。 为了支持该要求和其他配置选项,添加了 。 它提供和属性。 该属性控制工厂在其缓存中维护的活动会话数(默认值为无限制)。 如果已达到阈值,则任何获取另一个会话的尝试都将阻塞,直到其中一个缓存会话可用或会话的等待时间到期(默认等待时间为 )。 该属性配置该值。​​cache-sessions​​​​CachingSessionFactory​​​​sessionCacheSize​​​​sessionWaitTimeout​​​​sessionCacheSize​​​​sessionCacheSize​​​​Integer.MAX_VALUE​​​​sessionWaitTimeout​

如果要缓存会话,请按前面所述配置默认会话工厂,然后将其包装在 的实例中,您可以在其中提供这些附加属性。 以下示例演示如何执行此操作:​​CachingSessionFactory​

<bean id="ftpSessionFactory" class="o.s.i.ftp.session.DefaultFtpSessionFactory">
<property name="host" value="localhost"/>
</bean>

<bean id="cachingSessionFactory" class="o.s.i.file.remote.session.CachingSessionFactory">
<constructor-arg ref="ftpSessionFactory"/>
<constructor-arg value="10"/>
<property name="sessionWaitTimeout" value="1000"/>
</bean>

前面的示例显示了设置为 to 和设置为 1 秒(其值以毫秒为单位)创建的​​CachingSessionFactory​​​​sessionCacheSize​​​​10​​​​sessionWaitTimeout​

从 Spring Integration 3.0 开始,提供了一种方法。 调用时,所有空闲会话将立即关闭,正在使用的会话在返回到缓存时将关闭。 新的会话请求会根据需要建立新会话。​​CachingConnectionFactory​​​​resetCache()​

从版本 5.1 开始,具有一个新属性。 如果为 true,则通过发送 NOOP 命令来测试会话,以确保它仍然处于活动状态;如果没有,它将从缓存中删除;如果缓存中没有活动会话,则会创建新会话。​​CachingSessionFactory​​​​testSession​

用​​RemoteFileTemplate​

从 Spring Integration 3.0 开始,在对象上提供了一个新的抽象。 该模板提供了发送、检索(作为)、删除和重命名文件的方法。 此外,还提供了一种方法,允许调用方在会话上执行多个操作。 在所有情况下,模板都会负责可靠地关闭会话。 有关更多信息,请参阅 Javadoc for RemoteFileTemplate。 FTP 有一个子类:。​​FtpSession​​​​InputStream​​​​execute​​​​FtpRemoteFileTemplate​

版本 4.1 添加了其他方法,包括 ,它提供对底层的访问,从而允许您访问低级 API。​​getClientInstance()​​​​FTPClient​

并非所有 FTP 服务器都能正确实现该命令。 有些返回不存在的路径的正结果。 当路径是文件并且它存在时,该命令可靠地返回名称。 但是,这不支持检查空目录是否存在,因为当路径是目录时,始终返回空列表。 由于模板不知道路径是否表示目录,因此当路径似乎不存在时(使用 时),它必须执行其他检查。 这会增加开销,需要向服务器发出多个请求。 从版本 4.1.9 开始,提供具有以下选项的属性:​​STAT <path>​​​​NLST​​​​NLST​​​​NLST​​​​FtpRemoteFileTemplate​​​​FtpRemoteFileTemplate.ExistsMode​

  • ​STAT​​:执行 FTP 命令 () 以检查路径是否存在。 这是默认设置,要求 FTP 服务器正确支持该命令(带路径)。STATFTPClient.getStatus(path)STAT
  • ​NLST​​:执行 FTP 命令 — 。 如果要测试作为文件完整路径的路径,请使用此选项。 它不适用于空目录。NLSTFTPClient.listName(path)
  • ​NLST_AND_DIRS​​:首先执行该命令,如果未返回任何文件,则回退到使用 临时切换工作目录的技术。 请参阅 FtpSession.exists() 了解更多信息。NLSTFTPClient.changeWorkingDirectory(path)

由于我们知道案例始终只查找文件(而不是目录),因此我们可以安全地使用 和 组件的模式。​​FileExistsMode.FAIL​​​​NLST​​​​FtpMessageHandler​​​​FtpOutboundGateway​

对于任何其他情况,可以扩展 以在重写的方法中实现自定义逻辑。​​FtpRemoteFileTemplate​​​​exist()​

从版本 5.0 开始,可以使用新方法。 此方法允许在相同的线程绑定 . 当您需要执行作为工作单元的多个高级操作时,这很有用。 例如,将其与命令实现一起使用,其中我们对提供的目录中的每个文件执行操作,并递归地为其子目录执行操作。 有关更多信息,请参阅 Javadoc。​​RemoteFileOperations.invoke(OperationsCallback<F, T> action)​​​​RemoteFileOperations​​​​Session​​​​RemoteFileTemplate​​​​AbstractRemoteFileOutboundGateway​​​​mput​​​​put​

用​​MessageSessionCallback​

从 Spring Integration 4.2 开始,您可以使用带有 ( 在 Java 中) 的实现来对上下文执行任何操作。 它可用于任何非标准或低级 FTP 操作,并允许从集成流定义和功能接口 (Lambda) 实施注入进行访问,如以下示例所示:​​MessageSessionCallback<F, T>​​​​<int-ftp:outbound-gateway/>​​​​FtpOutboundGateway​​​​Session<FTPFile>​​​​requestMessage​

@Bean
@ServiceActivator(inputChannel = "ftpChannel")
public MessageHandler ftpOutboundGateway(SessionFactory<FTPFile> sessionFactory) {
return new FtpOutboundGateway(sessionFactory,
(session, requestMessage) -> session.list(requestMessage.getPayload()));
}

另一个示例可能是对正在发送或检索的文件数据进行预处理或后处理。

使用 XML 配置时,提供了一个属性来指定 Bean 名称。​​<int-ftp:outbound-gateway/>​​​​session-callback​​​​MessageSessionCallback​

与 和 属性互斥。 使用 Java 进行配置时,FtpOutboundGateway​ 类中提供了不同的构造函数。​​session-callback​​​​command​​​​expression​

Apache Mina FTP 服务器事件

在 5.2 版中添加的侦听某些 Apache Mina FTP 服务器事件,并将它们发布为 s,任何 bean、bean 方法或事件入站通道适配器都可以接收这些事件。​​ApacheMinaFtplet​​​​ApplicationEvent​​​​ApplicationListener​​​​@EventListener​

目前,支持的事件包括:

  • ​SessionOpenedEvent​​- 客户端会话已打开
  • ​DirectoryCreatedEvent​​- 已创建目录
  • ​FileWrittenEvent​​- 文件已写入
  • ​PathMovedEvent​​- 文件或目录已重命名
  • ​PathRemovedEvent​​- 删除了文件或目录
  • ​SessionClosedEvent​​- 客户端已断开连接

其中每个都是 的子类;您可以配置单个侦听器以接收所有事件类型。 每个事件的属性都是 ,您可以从中获取客户端地址等信息;在抽象事件上提供了一种方便的方法。​​ApacheMinaFtpEvent​​​​source​​​​FtpSession​​​​getSession()​

会话打开/关闭以外的事件具有另一个属性,该属性具有命令和参数等属性。​​FtpRequest​

要使用侦听器(必须是 Spring Bean)配置服务器,请将其添加到服务器工厂:

FtpServerFactory serverFactory = new FtpServerFactory();
...
ListenerFactory factory = new ListenerFactory();
...
serverFactory.addListener("default", factory.createListener());
serverFactory.setFtplets(new HashMap<>(Collections.singletonMap("springFtplet", apacheMinaFtpletBean)));
server = serverFactory.createServer();
server.start();

要使用 Spring 集成事件适配器使用这些事件,请执行以下操作:

@Bean
public ApplicationEventListeningMessageProducer eventsAdapter() {
ApplicationEventListeningMessageProducer producer =
new ApplicationEventListeningMessageProducer();
producer.setEventTypes(ApacheMinaFtpEvent.class);
producer.setOutputChannel(eventChannel());
return producer;
}

远程文件信息

从版本 5.2 开始,(FTP 流入站通道适配器)、(FTP 入站通道适配器)和 (FTP 出站网关)的“read”命令在消息中提供其他标头,以生成有关远程文件的信息:​​FtpStreamingMessageSource​​​​FtpInboundFileSynchronizingMessageSource​​​​FtpOutboundGateway​

  • ​FileHeaders.REMOTE_HOST_PORT​​- 在文件传输操作期间远程会话连接到的主机:端口对;
  • ​FileHeaders.REMOTE_DIRECTORY​​- 已执行操作的远程目录;
  • ​FileHeaders.REMOTE_FILE​​- 远程文件名;仅适用于单个文件操作。

由于 不会针对远程文件生成消息,而是使用本地副本,因此在同步操作期间,会将有关远程文件的信息存储在 (可以在外部配置) 中的 URI 样式 ()。 轮询本地文件时,将检索此元数据。 删除本地文件时,建议删除其元数据条目。 为此提供了一个回调。 此外,还有一个要在元数据键中使用的。 当这些组件之间共享同一实例时,建议使此前缀与基于 -的实现中使用的前缀不同,以避免条目覆盖,因为两者都筛选并使用相同的本地文件名作为元数据条目键。​​FtpInboundFileSynchronizingMessageSource​​​​AbstractInboundFileSynchronizer​​​​MetadataStore​​​​protocol://host:port/remoteDirectory#remoteFileName​​​​FtpInboundFileSynchronizingMessageSource​​​​AbstractInboundFileSynchronizer​​​​removeRemoteFileMetadata()​​​​setMetadataStorePrefix()​​​​MetadataStore​​​​FileListFilter​​​​MetadataStore​​​​AbstractInboundFileSynchronizer​