SFTP Outbound Channel Adapter

  • 使用临时文件名避免传输部分文件。 该适配器可以通过 XML 配置或 Java 配置(Java DSL)进行配置。

SFTP 出站通道适配器是一个连接到远程目录并为其作为传入 Message 有效负载接收的每个文件启动文件传输的特殊 MessageHandler。它还支持文件的多种表示形式,因此你不仅仅局限于 File 对象。与 FTP 出站适配器类似,SFTP 出站通道适配器支持以下有效负载:

The SFTP outbound channel adapter is a special MessageHandler that connects to the remote directory and initiates a file transfer for every file it receives as the payload of an incoming Message. It also supports several representations of the file so that you are not limited to the File object. Similar to the FTP outbound adapter, the SFTP 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

以下示例展示如何配置 SFTP 出站通道适配器:

The following example shows how to configure an SFTP outbound channel adapter:

<int-sftp:outbound-channel-adapter id="sftpOutboundAdapter"
    session-factory="sftpSessionFactory"
    channel="inputChannel"
    charset="UTF-8"
    remote-file-separator="/"
    remote-directory="foo/bar"
    remote-filename-generator-expression="payload.getName() + '-mysuffix'"
    filename-generator="fileNameGenerator"
    use-temporary-filename="true"
    chmod="600"
    mode="REPLACE"/>

有关这些属性的更多详细信息,请参见 schema

See the schema for more detail on these attributes.

SpEL and the SFTP Outbound Adapter

与Spring Integration中的许多其他组件一样,当您通过指定两个属性:remote-directory-expression`和`remote-filename-generator-expression(described earlier)配置SFTP出站通道适配器时,可以使用Spring表达式语言(SpEL)。表达式求值上下文将消息作为其根对象,这样就可以使用基于消息中的数据(来自“有效负载”或“标头”)动态计算文件名或现有目录路径的表达式。在前面的示例中,我们使用表达式值定义`remote-filename-generator-expression`属性,该值根据其原始名称计算文件名,同时添加后缀:'-mysuffix'。

As with many other components in Spring Integration, you can use the Spring Expression Language (SpEL) when you configure an SFTP outbound channel adapter by specifying two attributes: remote-directory-expression and remote-filename-generator-expression (described earlier). The expression evaluation context has the message as its root object, which lets you use expressions that can dynamically compute the file name or the existing directory path based on the data in the message (from either the 'payload' or the 'headers'). In the preceding example, we define the remote-filename-generator-expression attribute with an expression value that computes the file name based on its original name while also appending a suffix: '-mysuffix'.

从版本 4.1 开始,可以在传输文件时指定 mode。默认情况下,现有文件将被覆盖。模式由 FileExistsMode 枚举定义,其中包括以下值:

Starting with version 4.1, you can specify the mode when you are 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

使用 IGNOREFAIL 时,文件不会被传输。FAIL 会引发异常,而 IGNORE 则会静默忽略传输(尽管会产生 DEBUG 日志条目)。

With IGNORE and FAIL, the file is not transferred. FAIL causes an exception to be thrown, while IGNORE silently ignores the transfer (although a DEBUG log entry is produced).

版本 4.3 引入了 chmod 属性,可用于在上传后更改远程文件权限。你可以使用传统的 Unix 八进制格式(例如,600 仅允许文件所有者读写)。在使用 Java 配置适配器时,可以使用 setChmodOctal("600")setChmod(0600)

Version 4.3 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).

Avoiding Partially Written Files

处理文件传输时常见的问题之一是处理部分文件。文件可能在传输实际完成之前出现在文件系统中。

One of the common problems when dealing with file transfers is the possibility of processing a partial file. A file might appear in the file system before its transfer is actually complete.

为了解决此问题,Spring Integration SFTP 适配器使用一种通用算法,其中文件在临时名称下传输,然后在完全传输后重命名。

To deal with this issue, Spring Integration SFTP adapters use a common algorithm in which files are transferred under a temporary name and than renamed once they are fully transferred.

默认情况下,每个正在传输的文件都将以附加后缀的形式出现在文件系统中,默认情况下为 .writing。你可以通过设置 temporary-file-suffix 属性来更改后缀。

By default, every file that is in the process of being transferred appear in the file system with an additional suffix, which, by default, is .writing. You can change 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:

@SpringBootApplication
@IntegrationComponentScan
public class SftpJavaApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context =
                    new SpringApplicationBuilder(SftpJavaApplication.class)
                        .web(false)
                        .run(args);
        MyGateway gateway = context.getBean(MyGateway.class);
        gateway.sendToSftp(new File("/foo/bar.txt"));
    }

    @Bean
    public SessionFactory<SftpClient.DirEntry> sftpSessionFactory() {
        DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
        factory.setHost("localhost");
        factory.setPort(port);
        factory.setUser("foo");
        factory.setPassword("foo");
        factory.setAllowUnknownKeys(true);
        factory.setTestSession(true);
        return new CachingSessionFactory<SftpClient.DirEntry>(factory);
    }

    @Bean
    @ServiceActivator(inputChannel = "toSftpChannel")
    public MessageHandler handler() {
        SftpMessageHandler handler = new SftpMessageHandler(sftpSessionFactory());
        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 = "toSftpChannel")
         void sendToSftp(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 with the Java DSL:

@SpringBootApplication
public class SftpJavaApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(SftpJavaApplication.class)
            .web(false)
            .run(args);
    }

    @Bean
    public IntegrationFlow sftpOutboundFlow() {
        return IntegrationFlow.from("toSftpChannel")
            .handle(Sftp.outboundAdapter(this.sftpSessionFactory, FileExistsMode.FAIL)
                         .useTemporaryFileName(false)
                         .remoteDirectory("/foo")
            ).get();
    }

}