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
相关产品推荐
相关产品推荐

