SFTP功能
This commit is contained in:
parent
5a3a1a5c52
commit
ca3d5fb9f3
@ -121,6 +121,11 @@
|
|||||||
<artifactId>poi-ooxml</artifactId>
|
<artifactId>poi-ooxml</artifactId>
|
||||||
<version>5.2.4</version>
|
<version>5.2.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.jcraft</groupId>
|
||||||
|
<artifactId>jsch</artifactId>
|
||||||
|
<version>0.1.55</version> <!-- 推荐使用 0.1.55+ -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|||||||
138
dsp/src/main/java/com/jsc/dsp/utils/SFTPConnector.java
Normal file
138
dsp/src/main/java/com/jsc/dsp/utils/SFTPConnector.java
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
package com.jsc.dsp.utils;
|
||||||
|
|
||||||
|
import com.jcraft.jsch.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SFTPConnector {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(SFTPConnector.class);
|
||||||
|
|
||||||
|
@Value("${sftp.host}")
|
||||||
|
private String host;
|
||||||
|
|
||||||
|
@Value("${sftp.port:22}") // SFTP 默认端口 22
|
||||||
|
private Integer port;
|
||||||
|
|
||||||
|
@Value("${sftp.username}")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@Value("${sftp.password}") // 支持密码认证(生产环境建议改用私钥)
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@Value("${sftp.timeout:30000}")
|
||||||
|
private Integer timeout; // 单位:毫秒
|
||||||
|
|
||||||
|
@Value("${sftp.strictHostKeyChecking:false}") // false 仅用于测试环境!
|
||||||
|
private boolean strictHostKeyChecking;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件到 SFTP 服务器(密码认证)
|
||||||
|
*
|
||||||
|
* @param inputStream 源文件流(方法内部负责关闭)
|
||||||
|
* @param remotePath 远程绝对路径,如 /upload/2024/file.pdf
|
||||||
|
* @return 上传成功返回 true
|
||||||
|
*/
|
||||||
|
public boolean uploadFile(InputStream inputStream, String remotePath) {
|
||||||
|
Session session = null;
|
||||||
|
ChannelSftp channelSftp = null;
|
||||||
|
try {
|
||||||
|
// 1. 初始化 JSch 会话
|
||||||
|
JSch jsch = new JSch();
|
||||||
|
session = jsch.getSession(username, host, port);
|
||||||
|
session.setPassword(password);
|
||||||
|
session.setTimeout(timeout);
|
||||||
|
|
||||||
|
// 2. 配置 SSH 连接参数(安全提示:生产环境必须启用 StrictHostKeyChecking 并配置 known_hosts)
|
||||||
|
Properties config = new Properties();
|
||||||
|
config.put("StrictHostKeyChecking", String.valueOf(strictHostKeyChecking));
|
||||||
|
session.setConfig(config);
|
||||||
|
|
||||||
|
// 3. 建立连接
|
||||||
|
session.connect();
|
||||||
|
channelSftp = (ChannelSftp) session.openChannel("sftp");
|
||||||
|
channelSftp.connect(timeout);
|
||||||
|
|
||||||
|
// 4. 确保目标目录存在
|
||||||
|
ensureDirectoryExists(channelSftp, remotePath);
|
||||||
|
|
||||||
|
// 5. 上传文件(JSch 会完整读取流,但不关闭流)
|
||||||
|
channelSftp.put(inputStream, remotePath);
|
||||||
|
log.info("SFTP 文件上传成功: {}", remotePath);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (JSchException | SftpException e) {
|
||||||
|
log.error("SFTP 上传失败 [host={}, path={}]: {}", host, remotePath, e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("SFTP 上传异常 [path={}]: {}", remotePath, e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
// 6. 资源清理(先关流,再关通道/会话)
|
||||||
|
closeQuietly(inputStream);
|
||||||
|
if (channelSftp != null && channelSftp.isConnected()) {
|
||||||
|
try {
|
||||||
|
channelSftp.disconnect();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("关闭 SFTP 通道异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (session != null && session.isConnected()) {
|
||||||
|
session.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归创建远程目录(基于 ChannelSftp)
|
||||||
|
*
|
||||||
|
* @param sftp SFTP 通道
|
||||||
|
* @param remotePath 完整远程文件路径(含文件名)
|
||||||
|
* @throws SftpException 目录创建失败时抛出
|
||||||
|
*/
|
||||||
|
private void ensureDirectoryExists(ChannelSftp sftp, String remotePath) throws SftpException {
|
||||||
|
String dirPath = extractDirectory(remotePath);
|
||||||
|
if ("/".equals(dirPath)) return;
|
||||||
|
|
||||||
|
String[] dirs = dirPath.split("/");
|
||||||
|
StringBuilder current = new StringBuilder();
|
||||||
|
for (String dir : dirs) {
|
||||||
|
if (dir.isEmpty()) continue;
|
||||||
|
current.append("/").append(dir);
|
||||||
|
try {
|
||||||
|
sftp.cd(current.toString()); // 尝试进入目录
|
||||||
|
} catch (SftpException e) {
|
||||||
|
sftp.mkdir(current.toString()); // 不存在则创建
|
||||||
|
sftp.cd(current.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从完整路径提取目录部分(如 /a/b/file.txt → /a/b)
|
||||||
|
*/
|
||||||
|
private String extractDirectory(String path) {
|
||||||
|
int lastSlash = path.lastIndexOf('/');
|
||||||
|
return (lastSlash <= 0) ? "/" : path.substring(0, lastSlash);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安静关闭输入流
|
||||||
|
*/
|
||||||
|
private void closeQuietly(InputStream is) {
|
||||||
|
if (is != null) {
|
||||||
|
try {
|
||||||
|
is.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.debug("关闭输入流时忽略异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user