org.dromara.hutool.extra.ssh.SshjSftp Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2023 looly([email protected])
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.extra.ssh;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.util.CharsetUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.extra.ftp.AbstractFtp;
import org.dromara.hutool.extra.ftp.FtpConfig;
import org.dromara.hutool.extra.ftp.FtpException;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.sftp.RemoteResourceInfo;
import net.schmizz.sshj.sftp.SFTPClient;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import net.schmizz.sshj.xfer.FileSystemFile;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.List;
/**
* 在使用jsch 进行sftp协议下载文件时,总是中文乱码,而该框架源码又不允许设置编码。故:站在巨人的肩膀上,此类便孕育而出。
*
*
* 基于sshj 框架适配。
* 参考:https://github.com/hierynomus/sshj
*
*
* @author youyongkun
* @since 5.7.19
*/
public class SshjSftp extends AbstractFtp {
private SSHClient ssh;
private SFTPClient sftp;
/**
* 构造,使用默认端口
*
* @param sshHost 主机
*/
public SshjSftp(final String sshHost) {
this(new FtpConfig(sshHost, 22, null, null, CharsetUtil.UTF_8));
}
/**
* 构造
*
* @param sshHost 主机
* @param sshUser 用户名
* @param sshPass 密码
*/
public SshjSftp(final String sshHost, final String sshUser, final String sshPass) {
this(new FtpConfig(sshHost, 22, sshUser, sshPass, CharsetUtil.UTF_8));
}
/**
* 构造
*
* @param sshHost 主机
* @param sshPort 端口
* @param sshUser 用户名
* @param sshPass 密码
*/
public SshjSftp(final String sshHost, final int sshPort, final String sshUser, final String sshPass) {
this(new FtpConfig(sshHost, sshPort, sshUser, sshPass, CharsetUtil.UTF_8));
}
/**
* 构造
*
* @param sshHost 主机
* @param sshPort 端口
* @param sshUser 用户名
* @param sshPass 密码
* @param charset 编码
*/
public SshjSftp(final String sshHost, final int sshPort, final String sshUser, final String sshPass, final Charset charset) {
this(new FtpConfig(sshHost, sshPort, sshUser, sshPass, charset));
}
/**
* 构造
*
* @param config FTP配置
* @since 5.3.3
*/
protected SshjSftp(final FtpConfig config) {
super(config);
init();
}
/**
* SSH 初始化并创建一个sftp客户端.
*
* @author youyongkun
* @since 5.7.18
*/
public void init() {
this.ssh = new SSHClient();
ssh.addHostKeyVerifier(new PromiscuousVerifier());
try {
ssh.connect(ftpConfig.getHost(), ftpConfig.getPort());
ssh.authPassword(ftpConfig.getUser(), ftpConfig.getPassword());
ssh.setRemoteCharset(ftpConfig.getCharset());
this.sftp = ssh.newSFTPClient();
} catch (final IOException e) {
throw new FtpException("sftp 初始化失败.", e);
}
}
@Override
public AbstractFtp reconnectIfTimeout() {
if (StrUtil.isBlank(this.ftpConfig.getHost())) {
throw new FtpException("Host is blank!");
}
try {
this.cd(StrUtil.SLASH);
} catch (final FtpException e) {
close();
init();
}
return this;
}
@Override
public boolean cd(final String directory) {
final String exec = String.format("cd %s", directory);
command(exec);
final String pwd = pwd();
return pwd.equals(directory);
}
@Override
public String pwd() {
return command("pwd");
}
@Override
public boolean mkdir(final String dir) {
try {
sftp.mkdir(dir);
} catch (final IOException e) {
throw new FtpException(e);
}
return containsFile(dir);
}
@Override
public List ls(final String path) {
final List infoList;
try {
infoList = sftp.ls(path);
} catch (final IOException e) {
throw new FtpException(e);
}
if (CollUtil.isNotEmpty(infoList)) {
return CollUtil.map(infoList, RemoteResourceInfo::getName);
}
return null;
}
@Override
public boolean delFile(final String path) {
try {
sftp.rm(path);
return !containsFile(path);
} catch (final IOException e) {
throw new FtpException(e);
}
}
@Override
public boolean delDir(final String dirPath) {
try {
sftp.rmdir(dirPath);
return !containsFile(dirPath);
} catch (final IOException e) {
throw new FtpException(e);
}
}
@Override
public boolean uploadFile(final String destPath, final File file) {
try {
sftp.put(new FileSystemFile(file), destPath);
return containsFile(destPath);
} catch (final IOException e) {
throw new FtpException(e);
}
}
@Override
public void download(final String path, final File outFile) {
try {
sftp.get(path, new FileSystemFile(outFile));
} catch (final IOException e) {
throw new FtpException(e);
}
}
@Override
public void recursiveDownloadFolder(final String sourcePath, final File destDir) {
final List files = ls(sourcePath);
if (files != null && !files.isEmpty()) {
files.forEach(path -> download(sourcePath + "/" + path, destDir));
}
}
@Override
public void close() {
try {
sftp.close();
ssh.disconnect();
} catch (final IOException e) {
throw new FtpException(e);
}
}
/**
* 是否包含该文件
*
* @param fileDir 文件绝对路径
* @return true:包含 false:不包含
* @author youyongkun
* @since 5.7.18
*/
public boolean containsFile(final String fileDir) {
try {
sftp.lstat(fileDir);
return true;
} catch (final IOException e) {
return false;
}
}
/**
* 执行Linux 命令
*
* @param exec 命令
* @return 返回响应结果.
* @author youyongkun
* @since 5.7.19
*/
public String command(final String exec) {
Session session = null;
try {
session = ssh.startSession();
final Session.Command command = session.exec(exec);
final InputStream inputStream = command.getInputStream();
return IoUtil.read(inputStream, DEFAULT_CHARSET);
} catch (final Exception e) {
throw new FtpException(e);
} finally {
IoUtil.closeQuietly(session);
}
}
}