【问题标题】:Watch remote directory for added files and stream it for reading data over SFTP监视远程目录以获取添加的文件并将其流式传输以通过 SFTP 读取数据
【发布时间】:2019-09-18 17:07:46
【问题描述】:

我想在远程机器上添加一个监视新添加的 CSV 文件或未读文件。识别文件后,根据文件名中的时间戳读取文件。该文件将使用流而不是处理本地计算机来读取。在读取文件时,将 _reading 附加到文件名并在读取文件后附加 _read。该文件将通过 SFTP 协议读取,我计划使用 spring 集成 sftp。如果在读取文件时出错或文件中的数据不符合预期,我想将该文件移动到子目录中。

我尝试轮询远程目录并读取一次 CSV 文件。一旦读取,我将从目录中删除文件。

 <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-sftp</artifactId>
            <version>5.1.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-core</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>

Spring boot version 2.0.3.RELEASE   
 @Bean
    public SessionFactory<ChannelSftp.LsEntry> sftpSessionFactory() {
        DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
        factory.setHost(hostname);
        factory.setPort(22);
        factory.setUser(username);
        factory.setPassword(password);
        factory.setAllowUnknownKeys(true);
        return new CachingSessionFactory<ChannelSftp.LsEntry>(factory);
    }

    @Bean
    public MessageSource<InputStream> sftpMessageSource() {
        SftpStreamingMessageSource messageSource = new SftpStreamingMessageSource(template());
        messageSource.setRemoteDirectory(path);
        messageSource.setFilter(compositeFilters());
        return messageSource;
    }

    public CompositeFileListFilter compositeFilters() {
        return new CompositeFileListFilter()
                .addFilter(new SftpRegexPatternFileListFilter(".*csv"));
    }

    @Bean
    public SftpRemoteFileTemplate template() {
        return new SftpRemoteFileTemplate(sftpSessionFactory());
    }

    @Bean
    public IntegrationFlow sftpOutboundListFlow() {
        return IntegrationFlows.from(this.sftpMessageSource(), e -> e.poller(Pollers.fixedDelay(5, TimeUnit.SECONDS)))
                .handle(Sftp.outboundGateway(template(), NLST, path).options(Option.RECURSIVE)))
                .filter(compositeFilters())
                .transform(sorter())
                .split()
                .handle(Sftp.outboundGateway(template(), GET, "headers['file_remoteDirectory'] + headers['file_remoteFile']").options(STREAM))
                .transform(csvToPojoTransformer())
                .handle(service())
                .handle(Sftp.outboundGateway(template(), MV, "headers['file_remoteDirectory'] + headers['file_remoteFile'] + _read"))
                .handle(after())
                .get();
    }

    @Bean
    public MessageHandler sorter() {
        return new MessageHandler() {
            @Override
            public void handleMessage(Message<?> message) throws MessagingException {
                List<String> fileNames = (List<String>) message.getPayload();
                Collections.sort(fileNames);
            }
        };
    }

    @Bean
    public MessageHandler csvToPojoTransformer() {
        return new MessageHandler() {
            @Override
            public void handleMessage(Message<?> message) throws MessagingException {
                InputStream streamData = (InputStream) message.getPayload();
                convertStreamtoObject(streamData, Class.class);
            }
        };
    }

    public List<?> convertStreamtoObject(InputStream inputStream, Class clazz) {
        HeaderColumnNameMappingStrategy ms = new HeaderColumnNameMappingStrategy();
        ms.setType(clazz);
        Reader reader = new InputStreamReader(inputStream);

        CsvToBean cb = new CsvToBeanBuilder(reader)
                .withType(clazz)
                .withMappingStrategy(ms)
                .withSkipLines(0)
                .withSeparator('|')
                .withThrowExceptions(true)
                .build();
        return cb.parse();
    }

    @Bean
    public MessageHandler service() {
        return new MessageHandler() {
            @Override
            public void handleMessage(Message<?> message) throws MessagingException {
                List<Class> csvDataAsListOfPojo = List < Class > message.getPayload();
                // use this
            }
        };
    }
    @Bean
    public ExpressionEvaluatingRequestHandlerAdvice after() {
        ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice();
        advice.setSuccessChannelName("success.input");
        advice.setOnSuccessExpressionString("payload + ' was successful'");
        advice.setFailureChannelName("failure.input");
        advice.setOnFailureExpressionString("payload + ' was bad, with reason: ' + #exception.cause.message");
        advice.setTrapException(true);
        return advice;
    }
    @Bean
    public IntegrationFlow success() {
        return f -> f.handle(System.out::println);
    }

    @Bean
    public IntegrationFlow failure() {
        return f -> f.handle(System.out::println);
    }

更新代码

【问题讨论】:

    标签: spring-integration sftp spring-integration-sftp


    【解决方案1】:

    对于复杂的场景(列表、移动、获取、删除等),您应该使用SFTP remote file gateways instead

    SFTP 出站网关提供一组有限的命令,可让您与远程 SFTP 服务器交互:

    ls(列出文件)

    nlst(列出文件名)

    获取(检索文件)

    mget(检索多个文件)

    rm(删除文件)

    mv(移动和重命名文件)

    put(发送文件)

    mput(发送多个文件)

    或者直接从您的代码中使用SftpRemoteFileTemplate

    编辑

    响应您的 cmets;你需要这样的东西

    • 入站通道适配器(带轮询器) - 返回目录名称
    • LS 网关
    • 过滤(删除所有已提取的文件)
    • 转换器(列表排序)
    • 分离器
    • GET 网关(流选项)
    • 转换器(csv 到 POJO)
    • 服务(流程 POJO)

    如果你添加

    • RM 网关

    在您的服务(删除远程文件)之后,您不需要过滤步骤。

    您可能会发现 Java DSL 更易于组装此流程...

    @Bean
    public IntegrationFlow flow() {
        return IntegrationFlows.from(() -> "some/dir", e -> e.poller(...))
            .handle(...) // LS Gateway
            .filter(...)
            .transform(sorter())
            .split
            .handle(...) // GET Gateway
            .transform(csvToPojoTransformer())
            .handle(myService())
            .get()
    }
    

    【讨论】:

    • 如何查看远程机器中新增的文件,并根据文件名中的时间戳读取?我使用过 NIO,但这在远程机器场景中也没有帮助。
    • 网关列表命令返回文件信息(时间戳等)。您不能“观看”,但可以不时进行轮询以检测新文件。
    • 好的,我将轮询更改,但会有多个名为 sample_ddmmyy_hhmm.csv 的文件,我想从最早的文件开始一个一个地读取文件。我已经设置了 messageSource.setMaxFetchSize(1);一次读取一个文件,但我怎样才能先读取最旧的文件。我是 SFTP 集成的新手,如果我问了一个琐碎的问题,请见谅。
    • 如我所说,改用网关;您可以列出所有远程文件,对它们进行排序,然后一次获取一个。
    • 好吧我试试这个,看来我得用网关操作一个一个来达到我的目的。像 ListGateway 一样可以列出目录中的所有文件,还有其他有用的网关接口吗?除此之外,我正在考虑使用 openCSV 将 CSV 转换为 bean 的转换器,我在获取流对象后这样做,我可以在转换器注释中做吗?
    猜你喜欢
    • 2014-10-10
    • 2014-02-10
    • 1970-01-01
    • 1970-01-01
    • 2016-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多