不使用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(); } }
错误原因排查
- API使用误区:报错提及的
getInputStream方法并非SFTPClient类的标准方法,即使当前代码改用open方式,仍存在API调用不规范的问题。 - 类导入缺失:仅导入
RemoteFile.RemoteFileInputStream,未导入net.schmizz.sshj.sftp.RemoteFile,导致运行时无法识别RemoteFile类型。 - 资源管理不严谨:
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
相关产品推荐
相关产品推荐

