0
votes

I'm building a little microservice to access files from a SFTP file server. I decided to use Spring Integration SFTP to get the job done. I'm new to Spring Integration and confused with how it all works.

My goal is to get a list of files a directory on a SFTP server and present them to the user interface. From there a user will select a file for download and I'll use the filename to stream the file from the SFTP server to the user interface.

I'm using the following code which does work.

Entire class to handle SFTP with SSH


@Slf4j
@Configuration
public class SftpConfig {
    @Value("${sftp.host}")
    private String sftpHost;
    @Value("${sftp.port:22}")
    private int sftpPort;
    @Value("${sftp.user}")
    private String sftpUser;
    @Value("${sftp.privateKey:#{null}}")
    private Resource sftpPrivateKey;
    @Value("${sftp.privateKeyPassphrase:}")
    private String sftpPrivateKeyPassphrase;
    @Value("${sftp.password:#{null}}")
    private String sftpPasword;
    @Value("${sftp.remote.directory:/}")
    private String sftpRemoteDirectory;

    @Bean
public SessionFactory<LsEntry> sftpSessionFactory() {
        DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
        factory.setHost(sftpHost);
        factory.setPort(sftpPort);
        factory.setUser(sftpUser);
        if (sftpPrivateKey != null) {
            factory.setPrivateKey(sftpPrivateKey);
            factory.setPrivateKeyPassphrase(sftpPrivateKeyPassphrase);
        } else {
            factory.setPassword(sftpPasword);
        }
        factory.setAllowUnknownKeys(true);
        return new CachingSessionFactory<>(factory);
    }

    @ServiceActivator(inputChannel = "ftpLS")
    @Bean
public SftpOutboundGateway getFtpLS() {
        SftpOutboundGateway gateway = new SftpOutboundGateway(sftpSessionFactory(), "ls", "'" + sftpRemoteDirectory + "' + payload");
        gateway.setOption(AbstractRemoteFileOutboundGateway.Option.NAME_ONLY);
        return gateway;
    }

    @ServiceActivator(inputChannel = "ftpGet")
    @Bean
public SftpOutboundGateway getFtpGet() {
        SftpOutboundGateway gateway = new SftpOutboundGateway(sftpSessionFactory(), "get", "'" + sftpRemoteDirectory + "' + payload");
        gateway.setOption(AbstractRemoteFileOutboundGateway.Option.STREAM);
        return gateway;
    }

    @MessagingGateway(defaultRequestChannel = "ftpLS")
    public interface FtpLS {
        List list(String directory);
    }

    @MessagingGateway(defaultRequestChannel = "ftpGet")
    public interface FtpGet {
        InputStream get(String fileName);
    }

}

Run

@Bean
public ApplicationRunner runner(SftpConfig.FtpLS ftpLS, SftpConfig.FtpGet ftpGet) {
    return args -> {
        List<String> list = ftpLS.list("139");
        System.out.println("Result:" + list);

        InputStream is = ftpGet.get("139/" + list.get(0));
        String theString = IOUtils.toString(is,"UTF-8");
        System.out.println("Result:" + theString);

    };
}

My number one question is this the correct approach?

Secondly, do I need two interfaces in order to use the two different SftpOutboundGateway's?

Lastly, is there a better way to pass in a dynamic directory name when doing a FtsGet? Right now I'm passing I'm concatenating 139 with the base directory in a string and passing it in through the interface.

1

1 Answers

2
votes

is this the correct approach?

Yes, the approach is correct. Although I would suggest do not use isSharedSession for the gateway since it might be used from different threads by different users.

do I need two interfaces?

No, you really can have one @MessagingGateway, but with several methods marked with their own @Gateway annotations.

is there a better way to pass in a dynamic directory?

No, your approach is correct. There is no something like working directory to switch automatically, like we can do in FTP.