SFTP使用JSCH庫連結不斷開解決方法
阿新 • • 發佈:2019-02-20
在處理銀行業務的時候,資料互動還使用老舊的sftp技術,最近在使用sftp的JSCH庫時發現,採用公私鑰方式連線server,總是無法斷開連線,研究發現這種模式下如果存在併發的連結時,disconnect方法實際不起左右,這樣會產生大量連結,最終造成無法再連結上;解決辦法採用Threadlocal映象複製方式,為每個連結建立單獨的工具物件
public class SftpUtil { private final static Logger log = LoggerFactory.getLogger(SftpUtil.class); /** SFTP */ public static final String SFTP = "sftp"; /** 通道 */ private ChannelSftp channel; /** session */ private Session session; /** 規避多執行緒併發不斷開問題 */ private static ThreadLocal<SftpUtil> sftpLocal = new ThreadLocal<SftpUtil>();/** * 獲取sftpchannel * * @param connectConfig 連線配置 * @return * @throws Exception * @throws JSchException */ private void init(ConnectConfig connectConfig) throws Exception { String host = connectConfig.getHost(); int port = connectConfig.getPort(); String userName = connectConfig.getUserName(); //建立JSch物件 JSch jsch = new JSch(); //新增私鑰(信任登入方式) if (StringUtils.isNotBlank(connectConfig.getPrivateKey())) { jsch.addIdentity(connectConfig.getPrivateKey()); } session = jsch.getSession(userName, host, port); if (log.isInfoEnabled()) { log.info(" JSCH Session created,sftpHost = {}, sftpUserName={}", host, userName); } //設定密碼 if (StringUtils.isNotBlank(connectConfig.getPassWord())) { session.setPassword(connectConfig.getPassWord()); } Properties config = new Properties(); config.put("StrictHostKeyChecking", "no"); session.setConfig(config); //設定超時 session.setTimeout(connectConfig.getTimeout()); //建立連線 session.connect(); if (log.isInfoEnabled()) { log.info("JSCH Session connected.sftpHost = {}, sftpUserName={}", host, userName); } //開啟SFTP通道 channel = (ChannelSftp) session.openChannel(SFTP); //建立SFTP通道的連線 channel.connect(); if (log.isInfoEnabled()) { log.info("Connected successfully to sftpHost = {}, sftpUserName={}", host, userName); } } /** * 是否已連線 * * @return */ private boolean isConnected() { return null != channel && channel.isConnected(); } /** * 獲取本地執行緒儲存的sftp客戶端 * * @return * @throws Exception */ public static SftpUtil getSftpUtil(ConnectConfig connectConfig) throws Exception { SftpUtil sftpUtil = sftpLocal.get(); if (null == sftpUtil || !sftpUtil.isConnected()) { sftpLocal.set(new SftpUtil(connectConfig)); } return sftpLocal.get(); } /** * 釋放本地執行緒儲存的sftp客戶端 */ public static void release() { if (null != sftpLocal.get()) { sftpLocal.get().closeChannel(); sftpLocal.set(null); } } /** * 建構函式 * <p> * 非執行緒安全,故許可權為私有 * </p> * * @throws Exception */ private SftpUtil(ConnectConfig connectConfig) throws Exception { super(); init(connectConfig); } /** * 關閉通道 * * @throws Exception */ public void closeChannel() { if (null != channel) { try { channel.disconnect(); } catch (Exception e) { log.error("關閉SFTP通道發生異常:", e); } } if (null != session) { try { session.disconnect(); } catch (Exception e) { log.error("SFTP關閉 session異常:", e); } } } /** * 下載檔案 * * @param downDir 下載目錄 * @param src 原始檔 * @param dst 儲存後的檔名稱或目錄 * @throws Exception */ public void downFile(String downDir, String src, String dst) throws Exception { channel.cd(downDir); channel.get(src, dst); } /** * 刪除檔案 * * @param filePath 檔案全路徑 * @throws SftpException */ public void deleteFile(String filePath) throws SftpException { channel.rm(filePath); } @SuppressWarnings("unchecked") public List<String> listFiles(String dir) throws SftpException { Vector<LsEntry> files = channel.ls(dir); if (null != files) { List<String> fileNames = new ArrayList<String>(); Iterator<LsEntry> iter = files.iterator(); while (iter.hasNext()) { String fileName = iter.next().getFilename(); if (StringUtils.equals(".", fileName) || StringUtils.equals("..", fileName)) { continue; } fileNames.add(fileName); } return fileNames; } return null; } }