Search code examples
javaspringspring-elspring-integration-sftp

Spring Integration FTP - Create dynamic directory with remote directory expression (Java config)


When using Spring Integration's SFTP Session Factory (with Java config), I'd like to set the remote SFTP server directory dynamically. The Spring docs say this is possible:

Spring Integration SFTP Adapters

SpEL and the SFTP Outbound Adapter

As with many other components in Spring Integration, you can benefit from the Spring Expression Language (SpEL) support when configuring an SFTP Outbound Channel Adapter, by specifying two attributes remote-directory-expression and remote-filename-generator-expression (see above). The expression evaluation context will have the Message as its root object, thus allowing you to provide expressions which can dynamically compute the file name or the existing directory path based on the data in the Message (either from payload or headers). In the example above we are defining 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: -foo.

But I'm having troubles implementing this. I can't seem to find a good example that uses Spring's SpEL expression language. The code below works, and sends my files to the root directory as it is below, or a specific directory if I enter one in the LiteralExpression. But I'd like to swap out the LiteralExpression expression with a SpelExpression that uses the "path" header, similar to what I've done with the "file" header which dynamically adjusts the filename being uploaded.

@Configuration
public class SftpConfig {

@Autowired
private SftpSettings sftpSettings;

@Bean
public SessionFactory<LsEntry> sftpSessionFactory() {
    DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
    factory.setHost(sftpSettings.getHostname());
    factory.setPort(sftpSettings.getPort());
    factory.setUser(sftpSettings.getUsername());
    factory.setPassword(sftpSettings.getPassword());
    factory.setAllowUnknownKeys(true);
    return new CachingSessionFactory<LsEntry>(factory);
}

@Bean
@ServiceActivator(inputChannel = "toSftpChannel")
public MessageHandler handler() {
    SftpMessageHandler handler = new SftpMessageHandler(sftpSessionFactory());
    handler.setAutoCreateDirectory(true);                               // automatically create the remote directory
    handler.setRemoteDirectoryExpression(new LiteralExpression(""));
    handler.setFileNameGenerator(new FileNameGenerator() {
        @Override
        public String generateFileName(Message<?> message) {
            return (String) message.getHeaders().get("filename");
        }
    });
    return handler;
}

@MessagingGateway
public interface UploadGateway {

    @Gateway(requestChannel = "toSftpChannel")
    void upload(@Payload byte[] file, @Header("filename") String filename, @Header("path") String path);
}
}

To initiate the file upload I execute this code

@Autowired
private UploadGateway gateway;

byte[] file = "test".getBytes();
path = "mydirectory";
filename = "myfilename";
gateway.upload(file, filename, path); // edited to correct parameter order error

Solution

  • The SpEL expression can be really complex and dynamic. For that purpose you have to declare parser:

    ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser()
    

    and use it for this expression parsing:

    handler.setRemoteDirectoryExpression(EXPRESSION_PARSER.parseExpression("headers['path']"));
    

    And the result Expression object will be evaluated against each request message.

    See more info in the Reference Manual.