简体   繁体   中英

How to configure spring integration adapters to get and put a file via SFTP

I'm trying to implement the following scenario using Spring Integration:

The input channel should poll a SFTP site to get a file storing it in a local "stfp-inbound" folder. The output channel should push an existing file in a local "sftp-outbound" folder to the SFTP site. I started with the input channel. It had worked so far but it is obviously very static.

This is my config so far:

@Component
public class SftpClient {
    private static final Logger LOG = LoggerFactory.getLogger(SftpClient.class);

@Bean
public IntegrationFlow sftpInboundFlow() {
    return IntegrationFlows
        .from(Sftp.inboundAdapter(sftpSessionFactory())
                .preserveTimestamp(true)
                .remoteDirectory("data")
                .regexFilter(".*\\.txt$")
//                    .localFilenameExpression("#this.toUpperCase() + '.a'")
                .localDirectory(new File("ftp-inbound")),
             e -> e.id("sftpInboundAdapter")
                .autoStartup(true)
                .poller(Pollers.fixedDelay(5000)))
        .handle(m -> System.out.println(m.getPayload()))
        .get();
}

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

@Bean
public SessionFactory<LsEntry> sftpSessionFactory() {
//      // with private key resource: catch MalformedURLException
//      try {
        DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
        factory.setHost("myHost");
        factory.setPort(22);
        factory.setUser("myUser");
        factory.setPassword("myPassword");
//          factory.setPrivateKey(new FileUrlResource("/Users/myUser/.ssh/id_rsa"));
//          factory.setPrivateKeyPassphrase("passphrase");
        factory.setAllowUnknownKeys(true);
        return new CachingSessionFactory<LsEntry>(factory);
//      }
//      catch (MalformedURLException e) {
//          throw new IllegalArgumentException("malformed URL");
//      }
}

I need advice to make a more dynamic approach. I imagin a component class with public methods sftpGet() and sftpPut() to get and put a file, whereas the channels are created by integration flows with the required parameters that made up a SFTP transfer: hostname, port, user, password, remote dir, local dir.

How can I achieve that?

I got a good hint with a similar TCP/IP channel scenario but I couldn't transform those solution to this SFTP scenario.

Please advice!

After taking in Gary's recommendations I did the foolowing dynamic Bean:

@Component
public class DynamicSftpTemplate implements InputStreamCallback {
    private static Logger LOG = LoggerFactory.getLogger(DynamicSftpTemplate.class);

    private String localDir;
    private String localFilename;

    public void getSftpFile(String sessionId, String host, int port, String user, String password,
            String remoteDir, String remoteFilename, String localDir, String localFilename) {
        LOG.debug("getSftpFile sessionId={}", sessionId);
        ioSftpFile(GET, host, port, user, password,
                remoteDir, remoteFilename, localDir, localFilename);
    }

    public void putSftpFile(String sessionId, String host, int port, String user, String password,
            String remoteDir, String remoteFilename, String localDir, String localFilename) {
        LOG.debug("putSftpFile sessionId={}", sessionId);
        ioSftpFile(PUT, host, port, user, password,
                remoteDir, remoteFilename, localDir, localFilename);
    }

    public void rmSftpFile(String sessionId, String host, int port, String user, String password,
            String remoteDir, String remoteFilename) {
        LOG.debug("rmSftpFile sessionId={}", sessionId);
        ioSftpFile(RM, host, port, user, password, remoteDir, remoteFilename, null, null);
    }

    private void ioSftpFile(SftpOperationType op, String host, int port, String user, String password,
            String remoteDir, String remoteFilename, String localDir, String localFilename) {
        LOG.debug("ioSftpFile op={}, host={}, port={}", op, host, port);
        LOG.debug("ioSftpFile user={}, password={}", user, password);
        SftpRemoteFileTemplate template = new SftpRemoteFileTemplate(sftpSessionFactory(host, port, user, password));
        template.setFileNameExpression(new LiteralExpression(remoteDir + "/" + remoteFilename));
        template.setRemoteDirectoryExpression(new LiteralExpression(remoteDir));

        //template.afterPropertiesSet();
        this.localDir = localDir;
        this.localFilename = localFilename;
        if (op == GET) {
            // template.get(buildGetMessage(remoteDir, remoteFilename), (InputStreamCallback) this);
            template.get(remoteDir + "/" + remoteFilename, this);
        }
        else if (op == PUT) {
            template.send(buildPutMessage(remoteDir, remoteFilename), FileExistsMode.REPLACE);          
        }
        else if (op == RM) {
            template.remove(remoteDir + "/" + remoteFilename);          
        }
        else {
            throw new IllegalArgumentException("invalid sftp operation, " + op);
        }
    }

    private DefaultSftpSessionFactory sftpSessionFactory(String host, int port, String user, String password) {
        LOG.debug("sftpSessionFactory");
        DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
        factory.setHost(host);
        factory.setPort(port);
        factory.setUser(user);
        factory.setPassword(password);
        factory.setAllowUnknownKeys(true);
        return factory;
    }

    private Message<?> buildPutMessage(String remoteDir, String remoteFilename) {
        return MessageBuilder.withPayload(new File(this.localDir + "/" + this.localFilename))
                .setHeader("file_remoteDirectory", remoteDir)
                .setHeader("file_remoteFile", remoteFilename)
                .build();
    }

    public void doWithInputStream(InputStream is) throws IOException {
        LOG.debug("doWithInputStream");
        String fullPathName = this.localDir + "/" + this.localFilename;
        FileWriter w = new FileWriter(fullPathName);
        IOUtils.copy(is, w, "UTF-8");
        LOG.debug("doWithInputStream file {} written", fullPathName);
        w.close();
        is.close();
    }
}

You can use SFTP Gateways to get and put files (amongst other operations).

Or, you can just use an SftpRemoteFileTemplate directly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM