在我的 Spring Boot 应用程序中,我必须从数据库读取大量数据,将其转换为 CSV 文件并将其上传到 SFTP 服务器。由于文件可能很大,我无法读取内存中的整个文件然后上传。它将被传输并附加到 SFTP 服务器上。
我使用 atmoz/sftp docker 容器作为我的 SFTP 服务器,它工作正常。
对于 Spring boot 客户端 - Spring 集成 - SFTP 出站通道适配器 对我来说似乎很有希望。使用以下代码,我可以将文件上传到 SFTP 服务器:
@SpringBootApplication
public class SftpIngApplication {
public static void main(String[] args) throws FileNotFoundException {
ConfigurableApplicationContext context =
new SpringApplicationBuilder(SftpIngApplication.class)
.web(WebApplicationType.SERVLET)
.run(args);
MyGateway gateway = context.getBean(MyGateway.class);
gateway.sendToSftp(new File("/home/john/sftp-ing/notes.txt"));
}
@Bean
public SessionFactory<SftpClient.DirEntry> sftpSessionFactory() {
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
factory.setHost("localhost");
factory.setPort(2222);
factory.setUser("foo");
factory.setPassword("pass");
factory.setAllowUnknownKeys(true);
return new CachingSessionFactory<>(factory);
}
@MessagingGateway
public interface MyGateway {
@Gateway(requestChannel = "toSftpChannel")
void sendToSftp(File file);
}
@Bean
public SftpRemoteFileTemplate sftpRemoteFileTemplate(SessionFactory<SftpClient.DirEntry> sftpSessionFactory) {
return new SftpRemoteFileTemplate(sftpSessionFactory);
}
@Bean
public IntegrationFlow sftpOutboundFlow(SessionFactory<SftpClient.DirEntry> sftpSessionFactory) {
return IntegrationFlow.from("toSftpChannel")
.handle(Sftp.outboundAdapter(sftpSessionFactory, FileExistsMode.REPLACE)
.useTemporaryFileName(false)
.remoteDirectory("upload")
).get();
}
}
但是此示例使用
new File("/home/john/sftp-ing/notes.txt")
来上传文件。只需将其替换为 FileInputStream 也会上传文件,但不会使用相同的名称和扩展名,即 bar.txt。文件上传为 67157c1b-439d-9f3b-f2be-88eff908e39c.msg
.
在我看来,流是开放的,有些可能需要关闭,但是我不确定最好的方法是什么。有人可以帮我解决这个问题吗?
在您的用例中使用
InputStream
作为传输到 SFTP 的有效负载确实是正确的。您还缺少一个要提供的文件名。
FileNameGenerator
中的默认RemoteFileTemplate
是一个DefaultFileNameGenerator
,逻辑如下:
public String generateFileName(Message<?> message) {
Object filename = evaluateExpression(this.expression, message);
if (filename instanceof String name && StringUtils.hasText(name)) {
return name;
}
Object payload = message.getPayload();
if (payload instanceof File file) {
return file.getName();
}
return message.getHeaders().getId() + ".msg";
}
提到的表达是这样的:
private volatile Expression expression =
new FunctionExpression<Message<?>>((message) -> message.getHeaders().get(FileHeaders.FILENAME));
由于您不提供该信息
FileHeaders.FILENAME
,您最终会得到基于消息 ID 生成的名称。
在将
FileHeaders.FILENAME
发送到您的 InputStream
之前,请考虑使用预期的文件名填充 sftpOutboundFlow
标头!