Java使用Jsch操作sftp
引入包
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.31</version> </dependency>
import cn.hutool.core.map.MapUtil; import cn.hutool.extra.ssh.SshjSftp; import io.netty.util.CharsetUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; import java.util.Map; @Configuration @EnableConfigurationProperties(SftpProperties.class) public class SftpConfiguration { private SftpProperties sftpProperties; private static Map<String, SftpProperties.SftpConfig> sshjSftpMap = MapUtil.newHashMap(); @Autowired public SftpConfiguration(SftpProperties sftpProperties){ this.sftpProperties = sftpProperties; } public static SshjSftp getSftp(String fileType){ SftpProperties.SftpConfig sftpConfig = sshjSftpMap.get(fileType); return new SshjSftp(sftpConfig.getSshHost(),sftpConfig.getSshPort(),sftpConfig.getSshUser(),sftpConfig.getSshPass()); } @PostConstruct public void initSshjSftpConfig(){ this.sftpProperties.getSftpConfigs().forEach(s -> { sshjSftpMap.put(s.getFileType(),s); }); } }
import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.List; import static cn.hutool.core.date.DatePattern.SIMPLE_MONTH_PATTERN; @Getter @Setter @Component @ConfigurationProperties(prefix = "test.sftp") public class SftpProperties { private String sysId; private String unitNo; private String tempPath = "./tempFile"; private List<SftpConfig> sftpConfigs; private CheckInConfig checkInConfig; private CheckOutConfig checkOutConfig; @Getter @Setter public static class SftpConfig{ private String fileType; private String sshHost; private Integer sshPort; private String sshUser; private String sshPass; } @Getter @Setter public static class CheckInConfig { private String scpolicyPath; private String returnFilePath; private String soundFilePath; private String syncBankPath; } @Getter @Setter public static class CheckOutConfig { private String nwReturnFileDetailPath; private String outFilePath; private String returnFileDetailPath; private String policyStatusPath; } public String getTempPath() { return StrUtil.format("{}/{}", tempPath, DateUtil.date().toString(SIMPLE_MONTH_PATTERN)); } public String getBaseTempPath() { return tempPath; } }
使用SshjSftp操作sftp 不分服务器上会出现pwd等命令不生效情况
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.ftp.FtpException; import cn.hutool.extra.ssh.SshjSftp; import com.hrfax.modules.cgb.domain.type.SftpFileTypeEnum; import net.schmizz.sshj.sftp.SFTPClient; import java.io.IOException; public class SshjSftpUtil { public static void downloadSftpFile(String fileType,String sourceFilePath,String targetFileTempPath){ SshjSftp sftp = SftpConfiguration.getSftp(fileType); try { sftp.reconnectIfTimeout(); //TODO 考虑sourceFilePath 是否存在 try{ sftp.ls(sourceFilePath); } catch(FtpException var6){ //log.error("ls目录不存在{}",scpolicyPath); return ; } CollUtil.emptyIfNull(sftp.ls(sourceFilePath)).forEach(fileName -> { String source = StrUtil.format("{}/{}",sourceFilePath,fileName); String target = StrUtil.format("{}/{}",targetFileTempPath,fileName); sftp.download(source, FileUtil.newFile(target)); }); } catch (FtpException e) { throw new FtpException("sftp 下载出现异常.", e); } finally { sftp.close(); } } }
使用Jsch时JschUtil.createSftp()有并发问题存在
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.lang.Filter; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.CharUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.ftp.FtpException; import cn.hutool.extra.ssh.JschRuntimeException; import cn.hutool.extra.ssh.JschUtil; import cn.hutool.extra.ssh.Sftp; import com.alibaba.fastjson.JSONObject; import com.jcraft.jsch.*; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; import java.util.*; import javax.annotation.PostConstruct; import java.io.File; import java.util.concurrent.Semaphore; import java.util.stream.Collectors; @Slf4j @Configuration @EnableConfigurationProperties(SftpProperties.class) public class JschSftpUtil { private SftpProperties sftpProperties; private static Map<String, SftpProperties.SftpConfig> jschSftpMap = MapUtil.newHashMap(); @Autowired public JschSftpUtil(SftpProperties sftpProperties){ this.sftpProperties = sftpProperties; } //region 弃用的方法,引起并发问题 /** * 获取sftp对象 * @param fileType * @return */ public static Sftp getSftp(String fileType){ // 根据文件类型获取SFTP配置信息 SftpProperties.SftpConfig sftpConfig = jschSftpMap.get(fileType); // 创建SFTP连接对象 Sftp sftp = JschUtil.createSftp(sftpConfig.getSshHost(), sftpConfig.getSshPort(), sftpConfig.getSshUser(), sftpConfig.getSshPass()); return sftp; } /** * 通过SFTP协议下载文件。 * * 该方法通过传入的文件类型,从预配置的SFTP属性中获取相应的配置信息,然后使用这些信息建立SFTP连接, * 并从指定的源路径下载文件到目标临时路径。 * * @param fileType Sftp账户信息的类型,用于从预配置的SFTP属性中获取相应的配置信息。 * @param sourceFile 在SFTP服务器上的源文件。 * @param targetFileTemp 目标文件的临时路径,文件将被下载到这个路径 包括文件名。 */ public static void downloadSftpFile1(String fileType,String sourceFile,String targetFileTemp){ Sftp sftp=getSftp(fileType); try { // //移除SFTP 对应的Session //JschSessionPool.INSTANCE.remove(sftp.getClient().getSession()); if(!sftp.exist(sourceFile)){ log.error("下载SFTP文件失败,文件{}不存在",sourceFile); return; } sftp.download(sourceFile, FileUtil.newFile(targetFileTemp)); } catch (Exception e) { String msg=StrUtil.format("下载SFTP文件失败fileType={},sourceFile={},targetFileTemp={},失败原因如下-开始:{}",fileType,sourceFile,targetFileTemp,e.getMessage()); log.error(msg,e); //e.printStackTrace(); log.error("下载SFTP文件失败-结束..."); } finally { sftp.close(); } } /** * 通过SFTP协议下载文件。 * * 该方法通过传入的文件类型,从预配置的SFTP属性中获取相应的配置信息,然后使用这些信息建立SFTP连接, * 并从指定的源路径下载文件到目标临时路径。 * * @param fileType Sftp账户信息的类型,用于从预配置的SFTP属性中获取相应的配置信息。 * @param sourceFilePath 在SFTP服务器上的源文件路径。 * @param targetFileTempPath 目标文件的临时路径,文件将被下载到这个路径。 */ public static void downloadSftpFiles1(String fileType,String sourceFilePath,String targetFileTempPath){ Sftp sftp=getSftp(fileType); try { if(!sftp.exist(sourceFilePath)){ log.error("下载SFTP文件失败,路径{}不存在",sourceFilePath); return; } CollUtil.emptyIfNull(sftp.ls(sourceFilePath)).forEach(fileName -> { String source = StrUtil.format("{}/{}",sourceFilePath,fileName); String target = StrUtil.format("{}/{}",targetFileTempPath,fileName); sftp.download(source, FileUtil.newFile(target)); }); } catch (Exception e) { String msg=StrUtil.format("下载SFTP文件失败fileType={},sourceFilePath={},targetFileTempPath={},失败原因如下-开始:{}",fileType,sourceFilePath,targetFileTempPath,e.getMessage()); log.error(msg,e); log.error("下载SFTP文件失败-结束..."); } finally { sftp.close(); } } /** * 根据文件类型从SFTP服务器下载指定扩展名的文件。 * * @param fileType 文件类型,用于获取SFTP配置。 * @param sourceFilePath SFTP服务器上的源文件路径。 * @param targetFileTempPath 目标文件的临时路径,下载的文件将保存在这里。 * @param extName 需要下载的文件的扩展名。 * @return 成功下载的文件名列表,如果下载失败或无文件可下载,则返回null。 */ public static List<String> downloadSftpFileByExt1(String fileType,String sourceFilePath,String targetFileTempPath,String extName){ Sftp sftp=getSftp(fileType); try { if(!sftp.exist(sourceFilePath)){ log.error("批量下载SFTP文件失败,路径{}不存在",sourceFilePath); return Collections.emptyList(); } List<String> doneNames=CollUtil.emptyIfNull(sftp.ls(sourceFilePath)).stream() .filter(fileName -> fileName.endsWith(extName)).map(fileName -> { String source = StrUtil.format("{}/{}",sourceFilePath,fileName); String target = StrUtil.format("{}/{}",targetFileTempPath,fileName); sftp.download(source, FileUtil.newFile(target)); return fileName; }).collect(Collectors.toList()); return doneNames; } catch (Exception e) { String msg=StrUtil.format("批量下载SFTP文件失败fileType={},sourceFilePath={},targetFileTempPath={},extName={},失败原因如下-开始:{}",fileType,sourceFilePath,targetFileTempPath,extName,e.getMessage()); log.error(msg,e); //e.printStackTrace(); log.error("批量下载SFTP文件失败-结束..."); return Collections.emptyList(); } finally { sftp.close(); } } /** * 通过SFTP协议上传文件。 * * 该方法通过接收文件类型、SFTP上传路径和本地临时文件作为参数,使用JSCH库连接到SFTP服务器并上传文件。 * 首先,根据文件类型从一个映射表中获取SFTP配置信息。然后,基于这些配置信息创建一个SFTP连接。 * 如果上传路径在SFTP服务器上不存在,方法会先创建这个目录。最后,执行文件上传操作,并在完成操作后关闭SFTP连接。 * * @param fileType 文件类型,用于查找SFTP配置信息。 * @param uploadSftpPath SFTP服务器上的目标上传路径。 * @param localTempFile 要上传的本地临时文件。 */ public static void uploadSftpFile1(String fileType, String uploadSftpPath, File localTempFile){ Sftp sftp=getSftp(fileType); try { mkdirs(uploadSftpPath,sftp); sftp.upload(uploadSftpPath, localTempFile); } catch (Exception e) { String msg=StrUtil.format("上传文件到SFTP失败fileType={},uploadSftpPath={},localTempFile={},失败原因如下-开始{}:",fileType,uploadSftpPath,localTempFile,e.getMessage()); log.error(msg,e); log.error("上传文件到SFTP失败-结束..."); } finally { sftp.close(); } } /** * 递归创建目录 */ private static void mkdirs(String path, Sftp sftp) { if(sftp.exist(path)){ return; } String subPath = path.substring(0, path.lastIndexOf("/")); mkdirs(subPath, sftp); sftp.mkdir(path); } /** * 通过SFTP协议上传文件。 * * 该方法通过接收文件类型、SFTP上传路径和本地临时文件作为参数,使用JSCH库连接到SFTP服务器并上传文件。 * 首先,根据文件类型从一个映射表中获取SFTP配置信息。然后,基于这些配置信息创建一个SFTP连接。 * 如果上传路径在SFTP服务器上不存在,方法会先创建这个目录。最后,执行文件上传操作,并在完成操作后关闭SFTP连接。 * * @param fileType 文件类型,用于查找SFTP配置信息。 * @param uploadSftpPath SFTP服务器上的目标上传路径。 * @param localTempFiles 要上传的本地临时文件集合。 */ public static void uploadSftpFile1(String fileType, String uploadSftpPath, List<File> localTempFiles){ Sftp sftp=getSftp(fileType); try { mkdirs(uploadSftpPath,sftp); CollUtil.emptyIfNull(localTempFiles).forEach(file -> { sftp.upload(uploadSftpPath, file); }); } catch (Exception e) { String msg=StrUtil.format("批量上传文件到SFTP失败fileType={},uploadSftpPath={},localTempFiles={},失败原因如下-开始:{}",fileType,uploadSftpPath,localTempFiles,e.getMessage()); log.error(msg,e); log.error("批量上传文件到SFTP失败-结束..."); } finally { sftp.close(); } } /** * 检查SFTP服务器上指定文件或目录是否存在。 * * @param fileType 文件类型标识,用于获取SFTP配置信息。 * @param pathOrFile SFTP服务器上的路径或文件名,可以是文件或目录。 * @return 如果指定的文件或目录存在,则返回true;否则返回false。 */ public static boolean existSftp1(String fileType,String pathOrFile) { Sftp sftp=getSftp(fileType); try { // 检查指定的文件或目录是否存在 return sftp.exist(pathOrFile); } catch (Exception e) { // 捕获并处理异常,设置堆栈跟踪,然后返回false String msg=StrUtil.format("检查SFTP服务器上指定文件或目录是否存在失败fileType={},pathOrFile={},失败原因如下-开始:{}",fileType,pathOrFile,e.getMessage()); log.error(msg,e); log.error("检查SFTP服务器上指定文件或目录是否存在失败-结束..."); return false; } finally { // 关闭SFTP连接 sftp.close(); } } /** * 通过SFTP协议列出指定路径下的文件。 * * @param fileType 文件类型,用于查找对应的SFTP配置。 * @param path SFTP服务器上的路径,用于列出文件。 * @return 返回指定路径下的文件列表,如果路径不存在或发生异常,则返回null。 */ public static List<String> lsSftpFiles1(String fileType,String path){ Sftp sftp=getSftp(fileType); try { if(!sftp.exist(path)){ log.info("检查SFTP服务器上指定文件或目录不存在fileType={},path={}。",fileType,path); return Collections.emptyList(); } return sftp.ls(path); } catch (Exception e) { String msg=StrUtil.format("SFTP服务器上指定文件或目录ls失败fileType={},path={},失败原因如下-开始:{}",fileType,path,e.getMessage()); log.error(msg,e); log.error("SFTP服务器上指定文件或目录ls失败-结束..."); return Collections.emptyList(); } finally { // 关闭SFTP连接 sftp.close(); } } //endregion @PostConstruct public void initJschSftpConfig(){ this.sftpProperties.getSftpConfigs().forEach(s -> { jschSftpMap.put(s.getFileType(),s); }); } }
使用Jsch以下方法解决了并发问题、inputstream is closed和ieof packet referred to nonexistent channel 1问题
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.lang.Filter; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.CharUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.ftp.FtpException; import cn.hutool.extra.ssh.JschRuntimeException; import cn.hutool.extra.ssh.JschUtil; import cn.hutool.extra.ssh.Sftp; import com.alibaba.fastjson.JSONObject; import com.jcraft.jsch.*; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; import java.util.*; import javax.annotation.PostConstruct; import java.io.File; import java.util.concurrent.Semaphore; import java.util.stream.Collectors; @Slf4j @Configuration @EnableConfigurationProperties(SftpProperties.class) public class JschSftpUtil { private SftpProperties sftpProperties; private static Map<String, SftpProperties.SftpConfig> jschSftpMap = MapUtil.newHashMap(); @Autowired public JschSftpUtil(SftpProperties sftpProperties){ this.sftpProperties = sftpProperties; } /** * 通过SFTP协议下载文件。 * * 该方法通过传入的文件类型,从预配置的SFTP属性中获取相应的配置信息,然后使用这些信息建立SFTP连接, jschSftpMap.put(s.getFileType(),s); * 并从指定的源路径下载文件到目标临时路径。 * * @param fileType Sftp账户信息的类型,用于从预配置的SFTP属性中获取相应的配置信息。 * @param sourceFile 在SFTP服务器上的源文件。 * @param targetFileTemp 目标文件的临时路径,文件将被下载到这个路径 包括文件名。 */ public static void downloadSftpFile(String fileType,String sourceFile,String targetFileTemp){ Session session=null; ChannelSftp sftp=null; try { SftpProperties.SftpConfig sftpConfig = jschSftpMap.get(fileType); session = JschUtil.createSession(sftpConfig.getSshHost(), sftpConfig.getSshPort(), sftpConfig.getSshUser(), sftpConfig.getSshPass()); Properties config = new Properties(); config.put("StrictHostKeyChecking", "no"); session.setConfig(config); sftp = JschUtil.openSftp(session); if(!isDirExist(sourceFile,sftp)){ log.error("下载SFTP文件失败,{}路径{}不存在",fileType,sourceFile); return; } String targetFilePath=FileUtil.getAbsolutePath(targetFileTemp); sftp.get(sourceFile,targetFilePath); } catch (Exception e) { String msg=StrUtil.format("下载SFTP文件失败fileType={},sourceFile={},targetFileTemp={},失败原因如下-开始:{}",fileType,sourceFile,targetFileTemp,e.getMessage()); log.error(msg,e); //e.printStackTrace(); log.error("下载SFTP文件失败-结束..."); } finally { if (Objects.nonNull(sftp) && sftp.isConnected()) { sftp.disconnect(); } if (Objects.nonNull(session) && session.isConnected()) { session.disconnect(); } } } /** * 通过SFTP协议下载文件。 * * 该方法通过传入的文件类型,从预配置的SFTP属性中获取相应的配置信息,然后使用这些信息建立SFTP连接, * 并从指定的源路径下载文件到目标临时路径。 * * @param fileType Sftp账户信息的类型,用于从预配置的SFTP属性中获取相应的配置信息。 * @param sourceFilePath 在SFTP服务器上的源文件路径。 * @param targetFileTempPath 目标文件的临时路径,文件将被下载到这个路径。 */ public static List<String> downloadSftpFiles(String fileType,String sourceFilePath,String targetFileTempPath){ Session session=null; ChannelSftp sftp=null; try { SftpProperties.SftpConfig sftpConfig = jschSftpMap.get(fileType); session = JschUtil.createSession(sftpConfig.getSshHost(), sftpConfig.getSshPort(), sftpConfig.getSshUser(), sftpConfig.getSshPass()); Properties config = new Properties(); config.put("StrictHostKeyChecking", "no"); session.setConfig(config); sftp = JschUtil.openSftp(session); if(!isDirExist(sourceFilePath,sftp)){ log.error("批量下载SFTP文件失败,{}路径{}不存在",fileType,sourceFilePath); return Collections.emptyList(); } ChannelSftp finalSftp = sftp; List<String> list=ls(sourceFilePath,(Filter)null,sftp); List<String> doneNames=CollUtil.emptyIfNull(list).stream() .map(fileName -> { String source = StrUtil.format("{}/{}",sourceFilePath,fileName); String target = StrUtil.format("{}/{}",targetFileTempPath,fileName); String targetPath=FileUtil.getAbsolutePath(target); try { finalSftp.get(source, targetPath); } catch (SftpException e) { throw new RuntimeException(e); } return fileName; }).collect(Collectors.toList()); return doneNames; } catch (Exception e) { String msg=StrUtil.format("批量下载SFTP文件失败fileType={},sourceFilePath={},targetFileTempPath={},失败原因如下-开始:{}",fileType,sourceFilePath,targetFileTempPath,e.getMessage()); log.error(msg,e); log.error("批量下载SFTP文件失败-结束..."); return Collections.emptyList(); } finally { if (Objects.nonNull(sftp) && sftp.isConnected()) { sftp.disconnect(); } if (Objects.nonNull(session) && session.isConnected()) { session.disconnect(); } } } /** * 根据文件类型从SFTP服务器下载指定扩展名的文件。 * * @param fileType 文件类型,用于获取SFTP配置。 * @param sourceFilePath SFTP服务器上的源文件路径。 * @param targetFileTempPath 目标文件的临时路径,下载的文件将保存在这里。 * @param extName 需要下载的文件的扩展名。 * @return 成功下载的文件名列表,如果下载失败或无文件可下载,则返回null。 */ public static List<String> downloadSftpFileByExt(String fileType,String sourceFilePath,String targetFileTempPath,String extName){ Session session=null; ChannelSftp sftp=null; try { SftpProperties.SftpConfig sftpConfig = jschSftpMap.get(fileType); session = JschUtil.createSession(sftpConfig.getSshHost(), sftpConfig.getSshPort(), sftpConfig.getSshUser(), sftpConfig.getSshPass()); Properties config = new Properties(); config.put("StrictHostKeyChecking", "no"); session.setConfig(config); sftp = JschUtil.openSftp(session); if(!isDirExist(sourceFilePath,sftp)){ log.error("批量下载SFTP文件失败,{}路径{}不存在",fileType,sourceFilePath); return Collections.emptyList(); } ChannelSftp finalSftp = sftp; List<String> list=ls(sourceFilePath,(Filter)null,sftp); List<String> doneNames=CollUtil.emptyIfNull(list).stream() .filter(fileName -> fileName.endsWith(extName)) .map(fileName -> { String source = StrUtil.format("{}/{}",sourceFilePath,fileName); String target = StrUtil.format("{}/{}",targetFileTempPath,fileName); String targetPath=FileUtil.getAbsolutePath(target); try { finalSftp.get(source, targetPath); } catch (SftpException e) { throw new RuntimeException(e); } return fileName; }).collect(Collectors.toList()); return doneNames; } catch (Exception e) { String msg=StrUtil.format("批量下载SFTP指定扩展名文件失败fileType={},sourceFilePath={},targetFileTempPath={},extName={},失败原因如下-开始:{}",fileType,sourceFilePath,targetFileTempPath,extName,e.getMessage()); log.error(msg,e); //e.printStackTrace(); log.error("批量下载SFTP指定扩展名文件失败-结束..."); return Collections.emptyList(); } finally { if (Objects.nonNull(sftp) && sftp.isConnected()) { sftp.disconnect(); } if (Objects.nonNull(session) && session.isConnected()) { session.disconnect(); } } } /** * 通过SFTP协议上传文件。 * * 该方法通过接收文件类型、SFTP上传路径和本地临时文件作为参数,使用JSCH库连接到SFTP服务器并上传文件。 * 首先,根据文件类型从一个映射表中获取SFTP配置信息。然后,基于这些配置信息创建一个SFTP连接。 * 如果上传路径在SFTP服务器上不存在,方法会先创建这个目录。最后,执行文件上传操作,并在完成操作后关闭SFTP连接。 * * @param fileType 文件类型,用于查找SFTP配置信息。 * @param uploadSftpPath SFTP服务器上的目标上传路径。 * @param localTempFile 要上传的本地临时文件。 */ public static void uploadSftpFile(String fileType, String uploadSftpPath, File localTempFile){ Session session=null; ChannelSftp sftp=null; String localTempPath=""; try{ SftpProperties.SftpConfig sftpConfig = jschSftpMap.get(fileType); session = JschUtil.createSession(sftpConfig.getSshHost(), sftpConfig.getSshPort(), sftpConfig.getSshUser(), sftpConfig.getSshPass()); Properties config = new Properties(); config.put("StrictHostKeyChecking", "no"); session.setConfig(config); sftp = JschUtil.openSftp(session); localTempPath=FileUtil.getAbsolutePath(localTempFile); mkdirs(uploadSftpPath,sftp); sftp.put(localTempPath,uploadSftpPath); }catch (Exception e) { String msg=StrUtil.format("上传文件到SFTP失败fileType={},uploadSftpPath={},localTempFile={},失败原因如下-开始{}:",fileType,uploadSftpPath,localTempPath,e.getMessage()); log.error(msg,e); log.error("上传文件到SFTP失败-结束..."); }finally { if (Objects.nonNull(sftp) && sftp.isConnected()) { sftp.disconnect(); } if (Objects.nonNull(session) && session.isConnected()) { session.disconnect(); } } } /** * 通过SFTP协议上传文件。 * * 该方法通过接收文件类型、SFTP上传路径和本地临时文件作为参数,使用JSCH库连接到SFTP服务器并上传文件。 * 首先,根据文件类型从一个映射表中获取SFTP配置信息。然后,基于这些配置信息创建一个SFTP连接。 * 如果上传路径在SFTP服务器上不存在,方法会先创建这个目录。最后,执行文件上传操作,并在完成操作后关闭SFTP连接。 * * @param fileType 文件类型,用于查找SFTP配置信息。 * @param uploadSftpPath SFTP服务器上的目标上传路径。 * @param localTempFile 要上传的本地临时文件。 */ public static void uploadSftpFiles(String fileType, String uploadSftpPath, List<File> localTempFiles){ Session session=null; ChannelSftp sftp=null; String localTempPaths=""; try{ SftpProperties.SftpConfig sftpConfig = jschSftpMap.get(fileType); session = JschUtil.createSession(sftpConfig.getSshHost(), sftpConfig.getSshPort(), sftpConfig.getSshUser(), sftpConfig.getSshPass()); Properties config = new Properties(); config.put("StrictHostKeyChecking", "no"); session.setConfig(config); sftp = JschUtil.openSftp(session); mkdirs(uploadSftpPath,sftp); for(File file:localTempFiles){ localTempPaths=localTempPaths+FileUtil.getAbsolutePath(file); sftp.put(FileUtil.getAbsolutePath(file), uploadSftpPath); } }catch (Exception e) { String msg=StrUtil.format("上传文件到SFTP多个文件失败fileType={},uploadSftpPath={},localTempFiles={},失败原因如下-开始{}:",fileType,uploadSftpPath,localTempPaths,e.getMessage()); log.error(msg,e); log.error("上传文件到SFTP失败-结束..."); }finally { if (Objects.nonNull(sftp) && sftp.isConnected()) { sftp.disconnect(); } if (Objects.nonNull(session) && session.isConnected()) { session.disconnect(); } } } /** * 检查SFTP服务器上指定文件或目录是否存在。 * * @param fileType 文件类型标识,用于获取SFTP配置信息。 * @param pathOrFile SFTP服务器上的路径或文件名,可以是文件或目录。 * @return 如果指定的文件或目录存在,则返回true;否则返回false。 */ public static boolean existSftp(String fileType,String pathOrFile){ Session session=null; ChannelSftp sftp=null; try{ SftpProperties.SftpConfig sftpConfig = jschSftpMap.get(fileType); session = JschUtil.createSession(sftpConfig.getSshHost(), sftpConfig.getSshPort(), sftpConfig.getSshUser(), sftpConfig.getSshPass()); Properties config = new Properties(); config.put("StrictHostKeyChecking", "no"); session.setConfig(config); sftp = JschUtil.openSftp(session); return isDirExist(pathOrFile,sftp); }catch (Exception e) { String msg=StrUtil.format("SFTP的exist失败fileType={},pathOrFile={},失败原因如下-开始:{}",fileType,pathOrFile,e.getMessage()); log.error(msg,e); return false; }finally { if (Objects.nonNull(sftp) && sftp.isConnected()) { sftp.disconnect(); } if (Objects.nonNull(session) && session.isConnected()) { session.disconnect(); } } } /** * 通过SFTP协议列出指定路径下的文件。 * * @param fileType 文件类型,用于查找对应的SFTP配置。 * @param path SFTP服务器上的路径,用于列出文件。 * @return 返回指定路径下的文件列表,如果路径不存在或发生异常,则返回null。 */ public static List<String> lsSftpFiles(String fileType,String path){ Session session=null; ChannelSftp sftp=null; try{ SftpProperties.SftpConfig sftpConfig = jschSftpMap.get(fileType); session = JschUtil.createSession(sftpConfig.getSshHost(), sftpConfig.getSshPort(), sftpConfig.getSshUser(), sftpConfig.getSshPass()); Properties config = new Properties(); config.put("StrictHostKeyChecking", "no"); session.setConfig(config); sftp = JschUtil.openSftp(session); if(!isDirExist(path,sftp)){ log.info("检查SFTP服务器上指定文件或目录不存在fileType={},path={}。",fileType,path); return Collections.emptyList(); } List<String> list= ls(path,(Filter)null,sftp); return list; } catch (Exception e) { //throw new RuntimeException(e); String msg=StrUtil.format("SFTP的ls失败fileType={},path={},失败原因如下-开始:{}",fileType,path,e.getMessage()); log.error(msg,e); return Collections.emptyList(); }finally { if (Objects.nonNull(sftp) && sftp.isConnected()) { sftp.disconnect(); } if (Objects.nonNull(session) && session.isConnected()) { session.disconnect(); } } } /** * 递归创建目录 */ private static void mkdirs(String path, ChannelSftp sftp) throws SftpException{ if(isDirExist(path,sftp)){ return; } String subPath = path.substring(0, path.lastIndexOf("/")); mkdirs(subPath, sftp); sftp.mkdir(path); } /** * 判断目录是否存在 * @param path * @param channelSftp * @return */ private static boolean isDirExist(String path,ChannelSftp channelSftp) { boolean isDirExistFlag = false; try { SftpATTRS sftpATTRS = channelSftp.lstat(path); if (StrUtil.isBlank(path)) { return false; } else if (sftpATTRS.isDir()) { return true; } else if (CharUtil.isFileSeparator(path.charAt(path.length() - 1))) { return false; }else { String fileName = FileUtil.getName(path); if (!".".equals(fileName) && !"..".equals(fileName)) { String dir = StrUtil.emptyToDefault(StrUtil.removeSuffix(path, fileName), "."); SftpATTRS sftpATTRS2 = channelSftp.lstat(dir); if (!sftpATTRS2.isDir()) { return false; } else { List names; try { names = ls(path,(Filter)null,channelSftp);//channelSftp.ls(dir); } catch (FtpException var6) { return false; } return containsIgnoreCase(names, fileName); } } else { return false; } } } catch (Exception e) { if (e.getMessage().toLowerCase().equals("no such file")) { log.error("path:" + path + ",no such file ERROR!!!"); } log.error("判断文件或路径是否存在时出错:",e); } return isDirExistFlag; } private static boolean containsIgnoreCase(List<String> names, String nameToFind) { if (CollUtil.isEmpty(names)) { return false; } else if (StrUtil.isEmpty(nameToFind)) { return false; } else { Iterator var2 = names.iterator(); String name; do { if (!var2.hasNext()) { return false; } name = (String)var2.next(); } while(!nameToFind.equalsIgnoreCase(name)); return true; } } private static List<String> ls(String path, Filter<ChannelSftp.LsEntry> filter,ChannelSftp channelSftp) { List<ChannelSftp.LsEntry> entries = lsEntries(path, filter,channelSftp); return CollUtil.isEmpty(entries) ? ListUtil.empty() : CollUtil.map(entries, ChannelSftp.LsEntry::getFilename, true); } private static List<ChannelSftp.LsEntry> lsEntries(String path, Filter<ChannelSftp.LsEntry> filter,ChannelSftp channelSftp) { List<ChannelSftp.LsEntry> entryList = new ArrayList(); try { channelSftp.ls(path, (entry) -> { String fileName = entry.getFilename(); if (!StrUtil.equals(".", fileName) && !StrUtil.equals("..", fileName) && (null == filter || filter.accept(entry))) { entryList.add(entry); } return 0; }); } catch (SftpException var5) { SftpException e = var5; if (!StrUtil.startWithIgnoreCase(e.getMessage(), "No such file")) { throw new JschRuntimeException(e); } } return entryList; } @PostConstruct public void initJschSftpConfig(){ this.sftpProperties.getSftpConfigs().forEach(s -> { jschSftpMap.put(s.getFileType(),s); }); } }