FTP Outbound Channel Adapter
FTP 出站通道适配器依赖于 MessageHandler
实现,该实现连接到 FTP 服务器,并为其在传入消息有效负载中接收的每个文件启动 FTP 传输。它还支持文件的多种表示形式,因此你不仅限于 java.io.File
类型的有效负载。FTP 出站通道适配器支持以下有效负载:
The FTP outbound channel adapter relies on a MessageHandler
implementation that connects to the FTP server and initiates an FTP transfer for every file it receives in the payload of incoming messages.
It also supports several representations of a file, so you are not limited only to java.io.File
-typed payloads.
The FTP outbound channel adapter supports the following payloads:
-
java.io.File
: The actual file object -
byte[]
: A byte array that represents the file contents -
java.lang.String
: Text that represents the file contents -
java.io.InputStream
: a stream of data to transfer to remote file -
org.springframework.core.io.Resource
: a resource for data to transfer to remote file
下面的示例展示了如何配置 outbound-channel-adapter
:
The following example shows how to configure an outbound-channel-adapter
:
<int-ftp:outbound-channel-adapter id="ftpOutbound"
channel="ftpChannel"
session-factory="ftpSessionFactory"
charset="UTF-8"
remote-file-separator="/"
auto-create-directory="true"
remote-directory-expression="headers['remote_dir']"
temporary-remote-directory-expression="headers['temp_remote_dir']"
filename-generator="fileNameGenerator"
use-temporary-filename="true"
chmod="600"
mode="REPLACE"/>
之前的配置显示了如何使用 outbound-channel-adapter`元素配置 FTP 出站通道适配器,同时还为各种属性(如 `filename-generator
(o.s.i.file.FileNameGenerator`策略接口的实现)、对 `session-factory`的引用以及其他属性)提供值。您还可以看到一些 `*expression`属性的示例,这些属性允许您使用 SpEL 配置设置,如 `remote-directory-expression
、temporary-remote-directory-expression`和 `remote-filename-generator-expression
(对 `filename-generator`的 SpEL 替代方法,如上例所示)。与允许使用 SpEL 的任何组件一样,可以通过“payload”和“headers”变量访问有效负载和消息标头。有关可用属性的更多详细信息,请参见 schema。
The preceding configuration shows how you can configure an FTP outbound channel adapter by using the outbound-channel-adapter
element while also providing values for various attributes, such as filename-generator
(an implementation of the o.s.i.file.FileNameGenerator
strategy interface), a reference to a session-factory
, and other attributes.
You can also see some examples of *expression
attributes that let you use SpEL to configure settings such as remote-directory-expression
, temporary-remote-directory-expression
, and remote-filename-generator-expression
(a SpEL alternative to filename-generator
, shown in the preceding example).
As with any component that allows the usage of SpEL, access to the payload and the message Headers is available through the 'payload' and 'headers' variables.
See the schema for more details on the available attributes.
默认情况下,如果没有指定文件名称生成器,Spring Integration 会使用 |
By default, if no file name generator is specified, Spring Integration uses |
定义某些值(如 remote-directory
)可能取决于平台或 FTP 服务器。例如,正如 [role="bare"][role="bare"]https://forum.spring.io/showthread.php?p=333478&posted=1#post333478 所报告的,在某些平台上,您必须在目录定义末尾添加一个斜杠(例如,remote-directory="/thing1/thing2/"
而不是 remote-directory="/thing1/thing2"
)。
Defining certain values (such as remote-directory
) might be platform- or FTP server-dependent.
For example, as was reported on [role="bare"]https://forum.spring.io/showthread.php?p=333478&posted=1#post333478, on some platforms, you must add a slash to the end of the directory definition (for example, remote-directory="/thing1/thing2/"
instead of remote-directory="/thing1/thing2"
).
从版本 4.1 开始,你可以在传输文件时指定 mode
。默认情况下,将覆盖现有文件。这些模式由 FileExistsMode
枚举定义,其中包括以下值:
Starting with version 4.1, you can specify the mode
when transferring the file.
By default, an existing file is overwritten.
The modes are defined by the FileExistsMode
enumeration, which includes the following values:
-
REPLACE
(default) -
REPLACE_IF_MODIFIED
-
APPEND
-
APPEND_NO_FLUSH
-
IGNORE
-
FAIL
IGNORE
和 FAIL
不会传输文件。FAIL
会导致抛出异常,而 IGNORE
会静默忽略传输(尽管会产生 DEBUG
日志条目)。
IGNORE
and FAIL
do not transfer the file.
FAIL
causes an exception to be thrown, while IGNORE
silently ignores the transfer (although a DEBUG
log entry is produced).
版本 5.2 引入了 chmod
属性,你可以使用该属性在上传后更改远程文件权限。你可以使用传统的 Unix 八进制格式(例如,600
只允许文件所有者读写)。在使用 Java 配置适配器时,可以使用 setChmodOctal("600")
或 setChmod(0600)
。仅在你 FTP 服务器支持 SITE CHMOD
子命令时才适用。
Version 5.2 introduced the chmod
attribute, which you can use to change the remote file permissions after upload.
You can use the conventional Unix octal format (for example, 600
allows read-write for the file owner only).
When configuring the adapter using java, you can use setChmodOctal("600")
or setChmod(0600)
.
Only applies if your FTP server supports the SITE CHMOD
subcommand.
Avoiding Partially Written Files
在处理文件传输时遇到的常见问题之一是可能处理部分文件。也就是说,文件可能在传输实际完成之前出现在文件系统中。
One of the common problems that arises when dealing with file transfers is the possibility of processing a partial file. That is, a file might appear in the file system before its transfer is actually complete.
为了解决这个问题,Spring Integration FTP 适配器使用了通用算法:在临时名称下传输文件,然后在完全传输完成后重命名它们。
To deal with this issue, Spring Integration FTP adapters use a common algorithm: Files are transferred under a temporary name and then renamed once they are fully transferred.
默认情况下,正在传输过程中的每个文件都会以一个附加后缀出现在文件系统中,默认情况下为 .writing
。你可以通过设置 temporary-file-suffix
属性来更改此后缀。
By default, every file that is in the process of being transferred appears in the file system with an additional suffix, which, by default, is .writing
.
You can change this suffix by setting the temporary-file-suffix
attribute.
然而,可能存在一些不希望使用此技术的情况(例如,服务器不允许重命名文件)。对于此类情况,可以通过将 use-temporary-file-name
设置为 false
(默认值为 true
)来禁用此功能。当此属性为 false
时,文件将以其最终名称写入,并且使用应用程序需要其他机制在访问文件之前检测文件是否完全上传。
However, there may be situations where you do not want to use this technique (for example, if the server does not permit renaming files).
For situations like this, you can disable this feature by setting use-temporary-file-name
to false
(the default is true
).
When this attribute is false
, the file is written with its final name and the consuming application needs some other mechanism to detect that the file is completely uploaded before accessing it.
Configuring with Java Configuration
以下 Spring Boot 应用程序显示了如何使用 Java 配置配置出站适配器的示例:
The following Spring Boot application shows an example of how to configure the outbound adapter with Java configuration:
@SpringBootApplication
@IntegrationComponentScan
public class FtpJavaApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context =
new SpringApplicationBuilder(FtpJavaApplication.class)
.web(false)
.run(args);
MyGateway gateway = context.getBean(MyGateway.class);
gateway.sendToFtp(new File("/foo/bar.txt"));
}
@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() {
FtpMessageHandler handler = new FtpMessageHandler(ftpSessionFactory());
handler.setRemoteDirectoryExpressionString("headers['remote-target-dir']");
handler.setFileNameGenerator(new FileNameGenerator() {
@Override
public String generateFileName(Message<?> message) {
return "handlerContent.test";
}
});
return handler;
}
@MessagingGateway
public interface MyGateway {
@Gateway(requestChannel = "toFtpChannel")
void sendToFtp(File file);
}
}
Configuring with the Java DSL
以下 Spring Boot 应用程序显示了如何使用 Java DSL 配置出站适配器的示例:
The following Spring Boot application shows an example of how to configure the outbound adapter using the Java DSL:
@SpringBootApplication
@IntegrationComponentScan
public class FtpJavaApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context =
new SpringApplicationBuilder(FtpJavaApplication.class)
.web(false)
.run(args);
MyGateway gateway = context.getBean(MyGateway.class);
gateway.sendToFtp(new File("/foo/bar.txt"));
}
@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 IntegrationFlow ftpOutboundFlow() {
return IntegrationFlow.from("toFtpChannel")
.handle(Ftp.outboundAdapter(ftpSessionFactory(), FileExistsMode.FAIL)
.useTemporaryFileName(false)
.fileNameExpression("headers['" + FileHeaders.FILENAME + "']")
.remoteDirectory(this.ftpServer.getTargetFtpDirectory().getName())
).get();
}
@MessagingGateway
public interface MyGateway {
@Gateway(requestChannel = "toFtpChannel")
void sendToFtp(File file);
}
}