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

 

posted @ 2024-08-26 11:09  杜子烟  阅读(147)  评论(0编辑  收藏  举报