FTP 出站网关
FTP 出站网关提供一组有限的命令来与远程 FTP 或 FTPS 服务器进行交互。 支持的命令包括:
-
ls
(列出文件) -
nlst
(列出文件名) -
get
(检索文件) -
mget
(检索文件) -
rm
(删除文件) -
mv
(移动/重命名文件) -
put
(发送文件) -
mput
(发送多个文件)
使用ls
命令
ls
列出远程文件并支持以下选项:
-
-1
:检索文件名列表。 默认是检索FileInfo
对象。 -
-a
:包括所有文件(包括以“.”开头的文件) -
-f
:不对列表进行排序 -
-dirs
:包括目录(默认情况下排除它们) -
-links
:包括符号链接(默认情况下,它们被排除在外) -
-R
:递归列出远程目录
此外,还提供了文件名过滤,其方式与inbound-channel-adapter
.
请参阅 FTP 入站通道适配器。
由ls
operation 是文件名列表或FileInfo
对象。
这些对象提供修改时间、权限和其他详细信息等信息。
远程目录ls
执行 action on 的命令在file_remoteDirectory
页眉。
当使用递归选项 (-R
)、fileName
包括任何 subdirectory 元素,表示文件的相对路径(相对于远程目录)。
如果-dirs
选项,则每个递归目录也会作为列表中的一个元素返回。
在这种情况下,建议您不要使用-1
选项,因为您无法区分文件和目录,而您可以使用FileInfo
对象。
从版本 4.3 开始,FtpSession
支持null
对于list()
和listNames()
方法。
因此,您可以省略expression
属性。
为方便起见,Java 配置有两个没有expression
论点。
或LS
,NLST
,PUT
和MPUT
命令null
被视为客户端工作目录,根据 FTP 协议。
所有其他命令都必须随expression
根据请求消息评估远程路径。
您可以使用FTPClient.changeWorkingDirectory()
函数,当您扩展DefaultFtpSessionFactory
并实施postProcessClientAfterConnect()
回调。
使用nlst
命令
版本 5 引入了对nlst
命令。
nlst
列出远程文件名,并且仅支持一个选项:
-
-f
:不对列表进行排序
由nlst
operation 是文件名列表。
远程目录nlst
执行 action on 的命令在file_remoteDirectory
页眉。
与-1
选项ls
命令,它使用LIST
命令、nlst
命令会发送一个NLST
命令发送到目标 FTP 服务器。
当服务器不支持LIST
(例如,由于安全限制)。
结果nlst
operation 是名称,没有其他详细信息。
因此,框架无法确定实体是否为目录,例如,无法执行筛选或递归列表。
使用get
命令
get
检索远程文件。
它支持以下选项:
-
-P
:保留远程文件的时间戳。 -
-stream
:将远程文件作为流检索。 -
-D
:传输成功后删除远程文件。 如果忽略传输,则不会删除远程文件,因为FileExistsMode
是IGNORE
并且本地文件已存在。
这file_remoteDirectory
header 提供远程目录名称,而file_remoteFile
header 提供文件名。
由get
作是File
对象或InputStream
当您使用-stream
选择。
这-stream
选项允许将文件作为流检索。
对于文本文件,一个常见的用例是将此作与文件拆分器或流转换器结合使用。
将远程文件作为流使用时,您负责关闭Session
在流被消费后。
为方便起见,Session
在closeableResource
header,您可以使用IntegrationMessageHeaderAccessor
以下示例演示如何使用 convenience 方法:
Closeable closeable = new IntegrationMessageHeaderAccessor(message).getCloseableResource();
if (closeable != null) {
closeable.close();
}
以下示例演示如何将文件作为流使用:
<int-ftp: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>
object(即List
之File
对象,每个对象代表一个检索到的文件)。
从版本 5.0 开始,如果FileExistsMode 是IGNORE ,则输出消息的有效负载不再包含由于文件已存在而未获取的文件。
以前,该列表包含所有文件,包括已存在的文件。 |
用于确定远程路径的表达式应生成以- 例如
somedir/
将在somedir
.
从版本 5.0 开始,递归mget
,结合新的FileExistsMode.REPLACE_IF_MODIFIED
模式,可用于定期在本地同步整个远程目录树。
此模式将本地文件的上次修改时间戳替换为远程时间戳,而不管-P
(保留时间戳)选项。
使用递归 (
-R )该模式将被忽略,并被假定为。
默认情况下,将检索整个远程树。
但是,可以通过提供 如果过滤了子目录,则不会对该子目录执行额外的遍历。 这 通常,您会使用 |
持久文件列表过滤器现在具有布尔属性forRecursion
.
将此属性设置为true
,还会设置alwaysAcceptDirectories
,这意味着出站网关 (ls
和mget
) 现在每次都始终遍历完整的目录树。
这是为了解决未检测到目录树深处更改的问题。
另外forRecursion=true
使文件的完整路径用作元数据存储键;这解决了以下问题:如果具有相同名称的文件在不同目录中多次出现,则过滤器无法正常工作。
重要说明:这意味着对于顶级目录下的文件,将无法找到持久性元数据存储中的现有键。
因此,该属性为false
默认情况下;这可能会在未来版本中更改。
从版本 5.0 开始,FtpSimplePatternFileListFilter
和FtpRegexPatternFileListFilter
可以通过设置alwaysAcceptDirectories
property 设置为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
(或 expression) 用于命名远程文件。
其他可用属性包括remote-directory
,temporary-remote-directory
及其*-expression
等价物:use-temporary-file-name
和auto-create-directory
.
有关更多信息,请参阅 schema documentation.
由put
作是String
这表示传输后文件在服务器上的完整路径。
版本 5.2 引入了chmod
属性,该属性在上传后更改远程文件权限。
您可以使用传统的 Unix 八进制格式(例如600
仅允许文件所有者的读写)。
使用 java 配置适配器时,您可以使用setChmod(0600)
.
仅当您的 FTP 服务器支持SITE CHMOD
子命令。
使用mput
命令
这mput
将多个文件发送到服务器,并且仅支持一个选项:
-
-R
:递归的。 发送目录及其子目录中的所有文件(可能已过滤)。
消息负载必须是java.io.File
(或String
) 表示本地目录。
从 5.1 版本开始,一组File
或String
也受支持。
此命令支持与put
命令.
此外,本地目录中的文件可以使用以下选项之一进行过滤mput-pattern
,mput-regex
,mput-filter
或mput-filter-expression
.
只要子目录本身通过过滤器,过滤器就可以使用递归。
未通过过滤器的子目录不会递归。
由mput
作是List<String>
object(即List
的远程文件路径)。
另请参阅出站网关部分成功 (mget
和mput
).
版本 5.2 引入了chmod
属性,该属性允许您在上传后更改远程文件权限。
您可以使用传统的 Unix 八进制格式(例如600
仅允许文件所有者的读写)。
使用 Java 配置适配器时,您可以使用setChmodOctal("600")
或setChmod(0600)
.
仅当您的 FTP 服务器支持SITE CHMOD
子命令。
使用rm
命令
这rm
命令删除文件。
这rm
命令中没有选项。
由rm
operation 为Boolean.TRUE
如果删除成功,或者Boolean.FALSE
否则。
这file_remoteDirectory
header 提供远程目录,而file_remoteFile
header 提供文件名。
使用mv
命令
这mv
命令移动文件。
这mv
命令中没有选项。
这expression
属性定义 “from” 路径,rename-expression
attribute 定义 “to” 路径。
默认情况下,rename-expression
是headers['file_renameTo']
.
此表达式的计算结果不得为 null 或空String
.
如有必要,将创建任何必要的远程目录。
结果消息的有效负载为Boolean.TRUE
.
这file_remoteDirectory
header 提供原始远程目录,而file_remoteFile
header 提供文件名。
新路径位于file_renameTo
页眉。
从版本 5.5.6 开始,remoteDirectoryExpression
可用于mv
命令。
如果 “from” 文件不是完整的文件路径,则remoteDirectoryExpression
用作远程目录。
这同样适用于“to”文件,例如,如果任务只是重命名某个目录中的远程文件。
有关 FTP 出站网关命令的其他信息
这get
和mget
命令支持local-filename-generator-expression
属性。
它定义了一个 SPEL 表达式,用于在传输过程中生成本地文件的名称。
评估上下文的根对象是请求消息。
这remoteFileName
变量,这对于mget
,也可用 — 例如local-filename-generator-expression="#remoteFileName.toUpperCase() + headers.something"
.
这get
和mget
命令支持local-directory-expression
属性。
它定义了一个 SPEL 表达式,用于在传输过程中生成本地目录的名称。
评估上下文的根对象是请求消息 but.
这remoteDirectory
变量,这对于mget
,也可用 — 例如:local-directory-expression="'/tmp/local/' + #remoteDirectory.toUpperCase() + headers.something"
.
此属性与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
channel 是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
operations 为REPLACE
.
为GET
和MGET
作,默认值为FAIL
.
从版本 5.0 开始,setWorkingDirExpression()
(working-dir-expression
in XML) 选项。FtpOutboundGateway
(<int-ftp:outbound-gateway>
在 XML 中)。
它允许您在运行时更改客户端工作目录。
根据请求消息计算表达式。
每次网关作后,都会恢复以前的工作目录。
使用 Java 配置进行配置
Spring 以下 Boot 应用程序显示了如何使用 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 下面的 Boot 应用程序显示了如何使用 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
)
当您对多个文件执行作时(通过使用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
由 gateway 抛出的derivedInput
之file1.txt
,subdir
和zoo.txt
和partialResults
之file1.txt
.
其cause
是另一个PartialSuccessException
跟derivedInput
之file2.txt
和file3.txt
和partialResults
之file2.txt
.