You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

不使用JSch,如何从SFTP客户端读取.bck格式文件内容?

问题:SFTP无法读取.bck文件内容

背景

需从SFTP读取两个.bck格式文件用于每日报表任务,文件每行格式示例:

|00000001|FirstName|LastName|Active|0001|00000000|
|00000002|FirstName|LastName|Incctive|0002|00000001|
|00000003|FirstName|LastName|Active|0003|00020002|

已成功建立SFTP连接并获取文件名列表:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE List PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<List>
  <String>SAPHR_POS_CAEEMD_20230722.txt1690068889231.bck</String>
  <String>SAPHR_POS_USEEMD_20230722.txt1690068894774.bck</String>
</List>

但读取文件内容时触发错误:

Exception running rule: BeanShell script error: bsh.EvalError: Sourced file: inline evaluation of: 
Author: user007 Date: 16-07-2023     Details: ********* . . . '' : Typed variable declaration : Error in method invocation: Method getInputStream(java.lang.String) not found in class'net.schmizz.sshj.sftp.SFTPClient' :
at Line: 48 : in file: inline evaluation of:
``   /*********** Author: user007  Date: 16-07-2023  Details: ********* . . . '' : sftp .getInputStream ( fileName )   BSF info: usersReport
at line: 0 column: columnNo

当前使用的代码:

import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.sftp.SFTPClient;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import net.schmizz.sshj.sftp.RemoteFile.RemoteFileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.IOUtils;

SSHClient ssh = new SSHClient();
ssh.addHostKeyVerifier(new PromiscuousVerifier());

try {
  ssh.connect("ServerName");
  ssh.authPassword("username", "password");

  SFTPClient sftp = ssh.newSFTPClient();
  List fileContentsList = new ArrayList();

  try {
    List files = sftp.ls("/");
    LocalDate yesterday = LocalDate.now().minusDays(1);
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
    boolean usFileFound = false;
    boolean caFileFound = false;

    for (file : files) {
      String fileName = file.getName();

      if (!file.isDirectory()) {
        if (fileName.contains("SAPHR_POS_CAEEMD_") && fileName.contains(yesterday.format(formatter)) && !caFileFound) {
          RemoteFile remoteFile = sftp.open(fileName); // Obtain the RemoteFile handle
          InputStream inputStream = new RemoteFileInputStream(remoteFile); // Get the input stream
          //List lines = Arrays.asList(fileContents.split("\n"));
          String fileContents = IOUtils.toString(inputStream, "UTF-8"); // Read contents as a string
          fileContentsList.add(fileContents); // Add the contents as string to the list
          caFileFound = true;
          remoteFile.close();

        } else if (fileName.contains("SAPHR_POS_USEEMD_") && fileName.contains(yesterday.format(formatter)) && !usFileFound) {
          RemoteFile remoteFile = sftp.open(fileName); 
          InputStream inputStream = new RemoteFileInputStream(remoteFile);
          String fileContents = IOUtils.toString(inputStream, "UTF-8");
          fileContentsList.add(fileContents);
          usFileFound = true;
          remoteFile.close();
        }
        if (caFileFound && usFileFound) {
          break; // Stop after both files found
        }
      }
    }
  } finally {
    sftp.close();
  }

  String result = String.join("\n", fileContentsList);
  return result;
} finally {
  try {
    ssh.disconnect();
  } catch (IOException e) {
    e.printStackTrace();
  }
}

错误原因排查

  1. API使用误区:报错提及的getInputStream方法并非SFTPClient类的标准方法,即使当前代码改用open方式,仍存在API调用不规范的问题。
  2. 类导入缺失:仅导入RemoteFile.RemoteFileInputStream,未导入net.schmizz.sshj.sftp.RemoteFile,导致运行时无法识别RemoteFile类型。
  3. 资源管理不严谨:InputStream未显式关闭,存在资源泄漏风险;文件打开未指定模式,可能引发权限异常。

修正方案

1. 补全必要导入

添加RemoteFile类的导入语句:

import net.schmizz.sshj.sftp.RemoteFile;

2. 规范文件读取逻辑

使用SSHJ官方推荐的RemoteFile.new InputStream()方法获取输入流,替代手动实例化RemoteFileInputStream。

3. 完善资源自动回收

采用try-with-resources语法自动关闭RemoteFile和InputStream,避免资源泄漏。

修正后的完整代码

import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.sftp.SFTPClient;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import net.schmizz.sshj.sftp.RemoteFile;
import net.schmizz.sshj.sftp.RemoteResourceInfo;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.IOUtils;

SSHClient ssh = new SSHClient();
ssh.addHostKeyVerifier(new PromiscuousVerifier());

try {
  ssh.connect("ServerName");
  ssh.authPassword("username", "password");

  // 使用try-with-resources自动关闭SFTPClient
  try (SFTPClient sftp = ssh.newSFTPClient()) {
    List<String> fileContentsList = new ArrayList<>();
    List<?> files = sftp.ls("/");
    LocalDate yesterday = LocalDate.now().minusDays(1);
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
    boolean usFileFound = false;
    boolean caFileFound = false;

    for (Object fileObj : files) {
      // 强制转换为RemoteResourceInfo,避免类型模糊问题
      RemoteResourceInfo file = (RemoteResourceInfo) fileObj;
      String fileName = file.getName();

      if (!file.isDirectory()) {
        if (fileName.contains("SAPHR_POS_CAEEMD_") && fileName.contains(yesterday.format(formatter)) && !caFileFound) {
          // 自动关闭RemoteFile和InputStream
          try (RemoteFile remoteFile = sftp.open(fileName, RemoteFile.OpenMode.READ);
               InputStream inputStream = remoteFile.new InputStream()) {
            String fileContents = IOUtils.toString(inputStream, "UTF-8");
            fileContentsList.add(fileContents);
            caFileFound = true;
          }
        } else if (fileName.contains("SAPHR_POS_USEEMD_") && fileName.contains(yesterday.format(formatter)) && !usFileFound) {
          try (RemoteFile remoteFile = sftp.open(fileName, RemoteFile.OpenMode.READ);
               InputStream inputStream = remoteFile.new InputStream()) {
            String fileContents = IOUtils.toString(inputStream, "UTF-8");
            fileContentsList.add(fileContents);
            usFileFound = true;
          }
        }
        if (caFileFound && usFileFound) {
          break;
        }
      }
    }

    return String.join("\n", fileContentsList);
  }
} catch (IOException e) {
  e.printStackTrace();
  return "";
} finally {
  try {
    if (ssh.isConnected()) {
      ssh.disconnect();
    }
  } catch (IOException e) {
    e.printStackTrace();
  }
}

额外优化点

  • 类型明确化:将无类型的List改为泛型List<String>,并强制转换文件对象为RemoteResourceInfo,避免BeanShell类型推断异常。
  • 显式权限控制:指定RemoteFile.OpenMode.READ只读模式打开文件,符合最小权限原则。
  • 异常统一处理:集中捕获IO异常,避免未处理异常导致脚本终止。

内容的提问来源于stack exchange,提问作者D C

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 03:25:54