'JSch's ChannelSftp doesn't download more than one file at a time

It's too frustrating that JSch doesn't throw exceptions with decent messages. I'm trying to download a set of files as InputStreams. The code to download the file is pretty simple:

  @Override
  @SneakyThrows
  public InputStream getInputStream(String path) {
    return channelSftp.get(path);
  }

I have this list of file URLs that I'm using to download InputStream and convert it to DataPages:

    List<DataPage> dataPages =
        files.stream()
            .map(
                fileName -> {
                  String fileURL = folderUrl[0] + "/" + fileName;
                  return client.getInputStream(fileURL);
                })
            .map(dataPageFunction)
            .collect(Collectors.toList());

The first file is downloaded successfully. The problem occurs when we get the second file. I enabled log to discover if I find out anything, but the only thing I got is:

2022-04-13T10:58:27.916Z INFO [Connect thread localhost session] i.s.c.e.c.SftpClient$JschLogger:158 Caught an exception, leaving main loop due to Socket closed

I tried a different way to get files:

  @SneakyThrows
  public Stream<InputStream> getInputStreams(String folderURL, String filePattern) {
    Vector<ChannelSftp.LsEntry> lsEntries = channelSftp.ls(folderURL + "/" + filePattern);
    return lsEntries.stream()
        .map(
            entry -> {
              Optional<InputStream> is = Optional.empty();
              try {
                is = Optional.of(channelSftp.get(folderURL + "/" + entry.getFilename()));
              } catch (SftpException e) {
                log.error(e.getMessage());
              }
              return is;
            })
        .filter(Optional::isPresent)
        .map(Optional::get);
  }

And as noticed previously, the first file was downloaded successfully. There were three files in total, the channelSftp.get method failed for second and third file.

The printed logs were:

2022-04-13T12:08:06.864Z ERROR [Test worker] i.s.c.e.c.SftpClient:132 
2022-04-13T12:08:08.316Z ERROR [Test worker] i.s.c.e.c.SftpClient:132

and the stack trace was nothing but:

4: 
    at app//com.jcraft.jsch.ChannelSftp._stat(ChannelSftp.java:2227)
    at app//com.jcraft.jsch.ChannelSftp._stat(ChannelSftp.java:2242)
    at app//com.jcraft.jsch.ChannelSftp.ls(ChannelSftp.java:1592)
    at app//com.jcraft.jsch.ChannelSftp.ls(ChannelSftp.java:1553)
.
.
.

I'd like to include my open and close config just incase:

  @SneakyThrows
  public void open() {
    JSch jsch = new JSch();
    JSch.setLogger(new JschLogger());
    Properties config = new Properties();
    config.put("StrictHostKeyChecking", "no");
    if (StringUtils.isNoneBlank(privateKey)) {
      privateKey = SSHUtils.toRSA(privateKey, passphrase);
      jsch.addIdentity(
          "",
          privateKey.getBytes(),
          null,
          Objects.nonNull(passphrase) ? passphrase.getBytes() : null);
    }

    session = jsch.getSession(username, server, Objects.nonNull(port) ? port : DEFAULT_PORT);
    session.setConfig(config);
    if (StringUtils.isNoneBlank(password)) session.setPassword(password);
    session.connect();
    channelSftp = (ChannelSftp) session.openChannel("sftp");
    channelSftp.connect();
  }

  @SneakyThrows
  public void close() {
    if (Objects.nonNull(channelSftp) && channelSftp.isConnected()) channelSftp.disconnect();
    if (Objects.nonNull(session) && session.isConnected()) session.disconnect();
  }


Solution 1:[1]

I learned that JSch doesn't go that well with streams. This problem can be fixed by simply looping over lsEntries.

  @SneakyThrows
  public List<InputStream> getInputStreams(String folderURL, String filePattern) {
    Vector<ChannelSftp.LsEntry> lsEntries = channelSftp.ls(folderURL + "/" + filePattern);
    List<InputStream> inputStreams = new ArrayList<>();
    for (LsEntry entry : lsEntries) {
      try {
        inputStreams.add(channelSftp.get(folderURL + "/" + entry.getFilename()));
      } catch (SftpException e) {
        log.error(e.getMessage());
      }
    }
    return inputStreams;
  }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Anju Holkar