Search code examples
spring-integrationspring-integration-dslspring-integration-sftp

Spring Integration: How to dynamically create subdir on sftp using IntegrationFlow


I have a use case for transfering files to sftp under certain subdirs that are created dynamically. I got this working using custom SftpMessageHandler method and a Gateway. But the issue with this approach was, it was not deleting local temp files after successful upload. To solve that, now I am using IntegrationFlow along with expression Advice (as below), this does remove local files, but I don't know how to create remote subDirs dynamically. I read about remote directory expression, but not sure how to use/implement it.

Any one resolved this issue? Any help is appreciated!

@Bean
public IntegrationFlow sftpOutboundFlow() {

    return IntegrationFlows.from("toSftpChannel")
              .handle(Sftp.outboundAdapter(this.sftpSessionFactory())
                      .remoteFileSeparator("/")
                      .useTemporaryFileName(false)
                      .remoteDirectory("/temp"), c -> c.advice(expressionAdvice(c)))
                                     .get();
}



@Bean
public Advice expressionAdvice(GenericEndpointSpec<FileTransferringMessageHandler<ChannelSftp.LsEntry>> c) {
    ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice();
    advice.setOnSuccessExpressionString("payload.delete()");
    advice.setOnFailureExpressionString("payload + ' failed to upload'");
    advice.setTrapException(true);
    return advice;
}

@MessagingGateway
public interface UploadGateway {
    @Gateway(requestChannel = "toSftpChannel")
    void upload(File file);
}

Solution

  • The Sftp.outboundAdapter() has these options for the remote directory:

    /**
     * Specify a remote directory path.
     * @param remoteDirectory the remote directory path.
     * @return the current Spec
     */
    public S remoteDirectory(String remoteDirectory) {
    }
    
    /**
     * Specify a remote directory path SpEL expression.
     * @param remoteDirectoryExpression the remote directory expression
     * @return the current Spec
     */
    public S remoteDirectoryExpression(String remoteDirectoryExpression) {
    }
    
    /**
     * Specify a remote directory path {@link Function}.
     * @param remoteDirectoryFunction the remote directory {@link Function}
     * @param <P> the expected payload type.
     * @return the current Spec
     */
    public <P> S remoteDirectory(Function<Message<P>, String> remoteDirectoryFunction) {
    }
    

    So, if the story is about a dynamic sub-directory, you can choose a remoteDirectoryExpression or remoteDirectory(Function) and calculate a target path against message or some bean in the application context.

    For example:

    .remoteDirectoryExpression("'rootDir/' + headers.subDir")
    

    Also bear in mind that for not existing directories you need to configure an .autoCreateDirectory(true), too.