JSch 的 ChannelSftp 一次不会下载多个文件

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

令人沮丧的是 JSch 没有抛出正常消息的异常。我正在尝试下载一组文件作为 InputStreams。下载文件的代码非常简单:

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

我有这个用于下载 InputStream 并将其转换为 DataPages 的文件 URL 列表:

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

第一个文件下载成功。当我们得到第二个文件时,问题就出现了。我启用了日志来发现我是否发现了什么,但我唯一得到的是:

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

我尝试了另一种获取文件的方法:

  @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);
  }

如前所述,第一个文件已成功下载。总共有三个文件,channelSftp.get第二个和第三个文件的方法失败。

打印的日志是:

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

堆栈跟踪只是:

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)
.
.
.

我想包括我的 openclose 配置以防万一:

  @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();
  }

我了解到 JSch 不太适合流。这个问题可以通过简单地遍历 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;
  }