Reputation: 503
I am building an api with Java Spring Boot and what I want to do is use an endpoint to download a file. The problem is that the api accesses the file through ssh. I don't want the api to download the file and then return it, what I want is to make a direct stream from ssh to the response. Is this possible?
To connect by ssh and get the file I am using JSch. The files I want to download can be up to 2 GB in size. Thank you
Edit: I finally did this and it worked. Many thanks to @Martin Prikryl for his help.
@RequestMapping(value = "/endpoint-test/download-file-stream", method = RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
public static void sendFileInResponse (HttpServletResponse response) throws Exception {
String path_file = "/opt/dir/file_test.txt"; //this path is inside the remote server
ConexionJSch new_conection_to_filesystem = new ConexionJSch();
ChannelSftp channelSftp = new_conection_to_filesystem.setupJsch();
channelSftp.connect();
SftpATTRS data = channelSftp.lstat(path_file);
long fsize = data.getSize();
try (InputStream inputStream = channelSftp.get(path_file)){
response.setContentType("application/octet-stream");
response.setContentLengthLong(fsize);
response.setHeader("Content-Disposition", "inline;filename=test.bam");
OutputStream outputStream = response.getOutputStream()
byte[] buff = new byte[2048];
int length = 0;
while ((length = inputStream.read(buff)) > 0) {
outputStream.write(buff, 0, length);
outputStream.flush();
}
inputStream.close();
response.setHeader("Cache-Control", "private");
response.setDateHeader("Expires", 0);
} finally{
channelSftp.exit();
}
}
public class ConexionJSch {
private final String remoteHost;
private final String username;
private final String password;
public ConexionJSch() {
this.remoteHost = "xxx.xxx.xxx.xxx";
this.username = "user";
this.password = "pass";
}
public ChannelSftp setupJsch() throws JSchException {
JSch jsch = new JSch();
jsch.setKnownHosts("/home/ubuntu_user/.ssh/known_hosts");
Session jschSession = jsch.getSession(username, remoteHost);
jschSession.setPassword(password);
jschSession.connect();
return (ChannelSftp) jschSession.openChannel("sftp");
}
}
Upvotes: 1
Views: 1731
Reputation: 202197
JSch has these two ChannelSftp.get
overloads that will help you:
Based on your other question (How to return an InputStream as a file in Spring Boot?), in Spring Boot, you use HttpServletResponse
API to stream the response (file). Combined with the ChannelSftp.get
that accepts OutputStream
, the code can be:
public static void sendFileInResponse(
HttpServletResponse response, ChannelSftp channelSftp)
throws IOException, SftpException {
response.setContentType("your_content_type");
response.setHeader("Content-Disposition", "inline;filename=your_file_name");
channelSftp.get("/path/your_file_name", response.getOutputStream());
outputStream.close();
}
This variant with OutputStream
API is way shorter than your version with the channelSftp.get()
InputStream
API.
Though after reading bit on Spring Boot, it seems that a code like above is blocking and the download on the client side will effectively start only after the SFTP download finished. And consequently the web server will have to keep whole file in memory. It seems that more efficient solution is StreamingResponseBody
.
Upvotes: 2