Spring Integration – Sending files over SFTP

Spring Integration is a powerful extension of the Spring Framework designed to support the implementation of enterprise integration patterns in a Spring-based application. It provides a set of components and APIs for building messaging solutions and integrating disparate systems within an application.

At its core, Spring Integration leverages the concept of message-driven architectures, where components communicate by sending and receiving messages through channels. It enables the implementation of complex integration scenarios by providing support for various communication protocols, message routing, transformation, and mediation.

Key components of Spring Integration include:

  1. Message: The fundamental unit of communication in Spring Integration, containing data and headers.
  2. Channel: Acts as a conduit for messages to flow between components. Channels can be direct, point-to-point, publish-subscribe, etc.
  3. Message Endpoint: Represents the integration logic that consumes or produces messages from or to the messaging system.
  4. Message Handler: Components responsible for processing incoming messages.
  5. Message Source: Represents an input channel adapter that generates messages.
  6. Message Transformer: Converts or transforms messages from one format to another.
  7. Message Router: Determines the path of the message based on predefined criteria.
  8. Message Filter: Evaluates messages and decides whether to allow them to pass through or not.
  9. Service Activator: Invokes a service or method with the message payload.

Lets see the steps for sending files over SFTP using Spring Integration.

i) Include the required below dependency

<dependency>
  <groupId>org.springframework.integration</groupId>
  <artifactId>spring-integration-sftp</artifactId>
</dependency>

ii) Configuration to set up integration flow

package com.example.springintegrationsftp.config;

import java.io.File;

import org.apache.sshd.sftp.client.SftpClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.annotation.Gateway;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.file.FileNameGenerator;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.sftp.outbound.SftpMessageHandler;
import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
import org.springframework.integration.sftp.session.SftpRemoteFileTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;

@Configuration
@EnableIntegration
public class SftpConfig {
	
	@Value("${sftp.host}")
	private  String host;
	@Value("${sftp.user}")
	private String user;
	@Value("${sftp.password}")
	private String password;

    @Bean
    public SessionFactory<SftpClient.DirEntry> sftpSessionFactory() {
        DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
        factory.setHost(host);
        factory.setPort(22);
        factory.setUser(user);
        factory.setPassword(password);
        factory.setAllowUnknownKeys(true);
        return new CachingSessionFactory<>(factory);
    }

    @Bean
    @ServiceActivator(inputChannel = "toSftpChannel")
    public MessageHandler handler() {
        SftpMessageHandler handler = new SftpMessageHandler(sftpSessionFactory());
        handler.setRemoteDirectoryExpression(new LiteralExpression("/"));
        handler.setLoggingEnabled(true);
        handler.setFileNameGenerator(new FileNameGenerator() {
            @Override
            public String generateFileName(Message<?> message) {
                if (message.getPayload() instanceof File) {
                    return ((File) message.getPayload()).getName();
                } else throw new IllegalArgumentException("File expected in message payload");
            }
        });
        return handler;
    }

    @MessagingGateway(asyncExecutor = "sftpAsync")
    public interface CustomGateway {
        @Gateway(requestChannel = "toSftpChannel")
        void sendToSftp(File file);
    }
    
    @Bean
    public AsyncTaskExecutor sftpAsync() {
    	SimpleAsyncTaskExecutor simpleAsyncTaskExecutor = new SimpleAsyncTaskExecutor();
    	simpleAsyncTaskExecutor.setThreadNamePrefix("sftp-exec-");
    	return simpleAsyncTaskExecutor;
    }
}

iii) Define Service to invoke the file transfer

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.stereotype.Service;
import com.example.springintegrationsftp.config.SftpConfig;

@Service
public class SftpService {

	@Autowired
	SftpConfig.CustomGateway sftpGateway;

    public void uploadFileToSftp() {    	
  	
    	sftpGateway.sendToSftp(new FileSystemResource("D://sftp.txt").getFile());
    	
    }
}

iv) Define REST API to invoke the file transfer

@RestController
public class SftpController {
	
	@Autowired
	SftpService service;

	@GetMapping("/sendFile")
	public void sendFile() {
	
		service.uploadFileToSftp();
	}
}

In the internet found this free SFTP server(https://sftpcloud.io/tools/free-sftp-server) which we will be using to test sending file over SFTP.

Now we can hit our endpoint http://localhost:8080/sendFile to initiate the SFTP of file.

After the API is invoked could see the file in the SFTP server using the any SFTP client like winscp.

For complete source code: https://github.com/MMahendravarman/Springboot_Examples

Leave a Reply

Your email address will not be published. Required fields are marked *