You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot流式入站通道适配器远程文件删除:连接提前关闭问题

解决流式SFTP入站通道适配器中Advice执行前连接提前关闭的问题

看起来你遇到的问题是:Spring Integration的流式SFTP入站通道适配器在读取完文件流后,会自动关闭SFTP会话,但你的adviceChain(比如删除远程文件的操作)还没执行完,导致后续操作因为没有可用会话而失败。这是流式适配器的默认行为——它设计为在流关闭时立即释放会话,避免资源占用,但这就和你需要在Advice中操作远程文件的需求冲突了。

下面给你两种可靠的解决方案:

方案一:用SftpRemoteFileTemplate在Advice中独立执行删除操作

这种方法最稳妥,因为SftpRemoteFileTemplate会借助你配置的CachingSessionFactory来管理会话(复用缓存的会话或创建新会话),完全不依赖适配器的会话生命周期。

步骤1:定义SftpRemoteFileTemplate Bean

先把模板类注入到Spring容器中:

@Bean
public SftpRemoteFileTemplate sftpRemoteFileTemplate(SessionFactory<LsEntry> sftpSessionFactory) {
    return new SftpRemoteFileTemplate(sftpSessionFactory);
}

步骤2:创建删除文件的Advice

你可以用Spring提供的ExpressionEvaluatingRequestHandlerAdvice快速实现,不用自定义类:

@Bean
public Advice sftpDeleteAdvice(SftpRemoteFileTemplate remoteFileTemplate) {
    ExpressionEvaluatingRequestHandlerAdvice deleteAdvice = new ExpressionEvaluatingRequestHandlerAdvice();
    // 利用SpEL表达式调用模板的删除方法,从消息头中获取远程文件路径
    deleteAdvice.setOnSuccessExpressionString("@sftpRemoteFileTemplate.remove(headers['file_remoteFile'])");
    // 可选:设置为true,即使删除失败也不会中断主流程
    deleteAdvice.setTrapException(true);
    return deleteAdvice;
}

步骤3:给入站适配器配置Advice链

把上面的Advice添加到你的流式适配器中:

@Bean
@InboundChannelAdapter(channel = "sftpInputChannel", poller = @Poller(fixedDelay = "10000"))
public MessageSource<InputStream> sftpStreamingInboundChannelAdapter(SessionFactory<LsEntry> sftpSessionFactory,
                                                                     Advice sftpDeleteAdvice) {
    SftpStreamingInboundChannelAdapter adapter = new SftpStreamingInboundChannelAdapter(sftpSessionFactory);
    adapter.setRemoteDirectory("/your/remote/directory");
    adapter.setFilter(new SftpSimplePatternFileListFilter("*.csv")); // 按需设置文件过滤规则
    // 配置Advice链
    adapter.setAdviceChain(Collections.singletonList(sftpDeleteAdvice));
    return adapter;
}

方案二:修改适配器的会话关闭策略(不推荐,需手动管理资源)

如果你非要复用适配器的会话,可以修改它的closeSessionOnClose属性为false,这样流关闭时不会立即关闭会话,但你需要手动在Advice中关闭会话,否则会导致会话泄漏:

@Bean
@InboundChannelAdapter(channel = "sftpInputChannel", poller = @Poller(fixedDelay = "10000"))
public MessageSource<InputStream> sftpStreamingInboundChannelAdapter(SessionFactory<LsEntry> sftpSessionFactory) {
    SftpStreamingInboundChannelAdapter adapter = new SftpStreamingInboundChannelAdapter(sftpSessionFactory);
    adapter.setRemoteDirectory("/your/remote/directory");
    adapter.setFilter(new SftpSimplePatternFileListFilter("*.csv"));
    // 关闭流时不自动关闭会话
    adapter.setCloseSessionOnClose(false);
    
    // 自定义Advice,手动关闭会话
    AbstractRequestHandlerAdvice closeSessionAdvice = new AbstractRequestHandlerAdvice() {
        @Override
        protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
            try {
                return callback.execute();
            } finally {
                // 从消息头中获取会话并关闭
                Session<LsEntry> session = message.getHeaders().get(SftpHeaders.SESSION, Session.class);
                if (session != null) {
                    session.close();
                }
            }
        }
    };
    
    adapter.setAdviceChain(Arrays.asList(closeSessionAdvice, yourDeleteAdvice));
    return adapter;
}

⚠️ 注意:这种方法需要你严格管理会话的关闭,稍有不慎就会导致SFTP连接泄漏,所以更推荐方案一。

关键提醒

  • 确保消息头中正确携带了远程文件路径:流式适配器默认会把远程文件路径放在file_remoteFile(对应FileHeaders.REMOTE_FILE_PATH)头中,你可以直接用SpEL或代码获取。
  • 如果你用的是CachingSessionFactory,它会自动管理会话的缓存和复用,不用担心频繁创建连接的性能问题。

内容的提问来源于stack exchange,提问作者sri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:33:11