springBoot 整合 SFTP
一、引入依赖
<!-- sftp -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
二、增加配置项
sftp:
protocol: xxxx
host: xxxxx
port: 22 # 端口
username: root
password: ******
三、编写SftpUtils
创建SFTP 连接
/**
* 创建SFTP 连接
*
* @param userName userName
* @param host host
* @param port port
* @param password password
* @return
* @throws JSchException
*/
public static ChannelSftp createSftp(String userName, String host, int port, String password) throws JSchException {
log.error("createSftp. userName:{}, host:{}, port:{}, password:{}", userName, host, port, password);
JSch jsch = new JSch();
// 获取session
Session session = jsch.getSession(userName, host, port);
// 设置密码
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
Channel channel = session.openChannel("sftp");
channel.connect();
return (ChannelSftp) channel;
}
关闭SFTP的连接
/**
* 关闭SFTP的连接
*
* @param channelSftp sftp
*/
public static void closeSftp(ChannelSftp channelSftp) {
try {
if (channelSftp != null) {
if (channelSftp.isConnected()) {
channelSftp.disconnect();
} else if (channelSftp.isClosed()) {
log.error("sftp is closed already.");
}
if (null != channelSftp.getSession()) {
channelSftp.getSession().disconnect();
}
}
} catch (JSchException e) {
e.printStackTrace();
}
}
判断文件路径在sftp服务器是否存在
/**
* 判断文件路径在sftp服务器是否存在
*
* @param sftp sftp
* @param directoryPath 文件夹路径 如: roster/test.html
* @return
*/
public static boolean isDirExist(ChannelSftp sftp, String directoryPath) {
if (StringUtils.isEmpty(directoryPath)) {
log.error("isDirExist directoryPath is empty.");
return false;
}
try {
// 判断文件在sftp服务器是否存在
SftpATTRS lstat = sftp.lstat(directoryPath);
if (lstat.isDir()) {
return true;
}
} catch (SftpException e) {
log.error("isDirExist error. e:{}", e.toString());
e.printStackTrace();
}
return false;
}
创建一个文件夹
/**
* 创建一个文件夹
*
* @param sftp sftp
* @param createPath 创建目录(以 / 结尾)
* @return
*/
public static boolean createDir(ChannelSftp sftp, String createPath) {
// 创建目录不为空
if (StringUtils.isEmpty(createPath)) {
log.error("createDir createPath is empty.");
return false;
}
// 创建目录以 / 结尾
boolean endsWith = createPath.endsWith("/");
if (!endsWith) {
log.error("createDir createPath endsWith is not 【/】.");
return false;
}
// 如果已经存在此目录则直接返回成功
if (isDirExist(sftp, createPath)) {
log.error("createDir createPath isDirExist is true.");
return true;
}
// 创建目录 mkdir
try {
String[] split = createPath.split("/");
StringBuilder sb = new StringBuilder();
for (String path : split) {
sb.append(path).append("/");
String sbPath = sb.toString();
if (!isDirExist(sftp, sbPath)) {
sftp.mkdir(sbPath);
}
}
return true;
} catch (SftpException e) {
log.error("createDir mkdir error. e:{}", e.toString());
e.printStackTrace();
}
return false;
}
删除一个文件夹
/**
* 删除一个文件夹
*
* @param sftp sftp
* @param deletePath 删除目录(以 / 结尾)
* @return
*/
public static boolean deleteDir(ChannelSftp sftp, String deletePath) {
// 删除目录不为空
if (StringUtils.isEmpty(deletePath)) {
log.error("deleteDir deletePath is empty.");
return false;
}
// 删除目录以 / 结尾
boolean endsWith = deletePath.endsWith("/");
if (!endsWith) {
log.error("deleteDir deletePath endsWith is not 【/】.");
return false;
}
// 如果不存在此目录则直接返回成功
if (!isDirExist(sftp, deletePath)) {
log.error("deleteDir deletePath isDirExist is false.");
return true;
}
// 删除
try {
sftp.rmdir(deletePath);
return true;
} catch (SftpException e) {
log.error("deleteDir rm error. e:{}", e.toString());
e.printStackTrace();
}
return false;
}
删除文件
/**
* 删除文件
*
* @param sftp sftp
* @param filePath /roster
* @param fileName /test.html
* @return
*/
public static boolean deleteFile(ChannelSftp sftp, String filePath, String fileName) {
// 参数校验
if (StringUtils.isEmpty(filePath) || StringUtils.isEmpty(fileName)) {
log.error("deleteFile path is empty. filePath:{}, filePath:{}", filePath, fileName);
return false;
}
// 目录 及 文件名称
String path = filePath + fileName;
log.error("deleteFile path:{}", path);
// 判断目录是否存在
if (!isDirExist(sftp, filePath)) {
log.error("deleteFile filePath isDirExist is false. path:{}", path);
return false;
}
// 删除
try {
sftp.rm(path);
return true;
} catch (SftpException e) {
log.error("deleteFile rm error. e:{}", e.toString());
e.printStackTrace();
}
return false;
}
文件上传
/**
* 文件上传
*
* @param sftp sftp
* @param fileStream 文件输入流
* @param uploadFilePath 上传文件夹路径
* @param uploadFileName 上传文件名称
* @return
*/
public static boolean uploadFile(ChannelSftp sftp, InputStream fileStream, String uploadFilePath, String uploadFileName) {
// 参数校验
if (Objects.isNull(fileStream) || StringUtils.isEmpty(uploadFilePath) || StringUtils.isEmpty(uploadFileName)) {
log.error("uploadFile data is empty. fileStream:{}, uploadFilePath:{}, uploadFileName:{}", fileStream, uploadFilePath, uploadFileName);
return false;
}
// 目录 及 文件名称
String path = uploadFilePath + uploadFileName;
log.error("uploadFile uploadFilePath:{}", path);
// 判断上传目录是否存在
if (!isDirExist(sftp, uploadFilePath)) {
log.error("uploadFile uploadFilePath isDirExist is false. path:{}", path);
return false;
}
// 上传
try {
sftp.put(fileStream, path);
return true;
} catch (SftpException e) {
log.error("uploadFile put error. e:{}", e.toString());
e.printStackTrace();
}
return false;
}
读取文件内容
/**
* 获取指定sftp服务器上 指定目录下 指定文件名(含后缀) 的文本内容
*
* @param sftp sftp
* @param filePath 文件目录
* @param fileName 文件名称
* @return
*/
public static String getTextFileContent(ChannelSftp sftp, String filePath, String fileName) {
// 目录 及 文件名称
String path = filePath + fileName;
log.error("getTextFileContent. path:{}", path);
//限制文件类型
List<String> fileTypeStrList = Lists.newArrayList("txt", "html");
String[] split = fileName.split("\\.");
String fileTypeStr = split[split.length - 1];
if (!fileTypeStrList.contains(fileTypeStr)) {
log.error("getTextFileContent fileTypeStr is not contains. path:{}", path);
return StringUtils.EMPTY;
}
StringBuilder stringBuilder = new StringBuilder();
try {
// 判断查看目录是否存在
if (!isDirExist(sftp, filePath)) {
log.error("getTextFileContent filePath isDirExist is false. path:{}", path);
return StringUtils.EMPTY;
}
// 获取input stream
InputStream inputStream = sftp.get(path);
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
String line;
BufferedReader br = new BufferedReader(inputStreamReader);
while ((line = br.readLine()) != null) {
stringBuilder.append(line);
}
} catch (SftpException | IOException e) {
log.error("getTextFileContent. e:{}", e.toString());
e.printStackTrace();
}
return stringBuilder.toString();
}
完整SftpUtils类
package com.joker.cloud.linserver.conf.sftp;
import com.google.common.collect.Lists;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
/**
* SftpUtils
*
* @author joker
* @version 1.0
* 2024/3/28 10:42
**/
@Slf4j
@Service
public class SftpUtils {
/**
* 创建SFTP 连接
*
* @param userName userName
* @param host host
* @param port port
* @param password password
* @return
* @throws JSchException
*/
public static ChannelSftp createSftp(String userName, String host, int port, String password) throws JSchException {
log.error("createSftp. userName:{}, host:{}, port:{}, password:{}", userName, host, port, password);
JSch jsch = new JSch();
// 获取session
Session session = jsch.getSession(userName, host, port);
// 设置密码
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
Channel channel = session.openChannel("sftp");
channel.connect();
return (ChannelSftp) channel;
}
/**
* 关闭SFTP的连接
*
* @param channelSftp sftp
*/
public static void closeSftp(ChannelSftp channelSftp) {
try {
if (channelSftp != null) {
if (channelSftp.isConnected()) {
channelSftp.disconnect();
} else if (channelSftp.isClosed()) {
log.error("sftp is closed already.");
}
if (null != channelSftp.getSession()) {
channelSftp.getSession().disconnect();
}
}
} catch (JSchException e) {
e.printStackTrace();
}
}
// 业务 =============
/**
* 获取指定sftp服务器上 指定目录下 指定文件名(含后缀) 的文本内容
*
* @param sftp sftp
* @param filePath 文件目录
* @param fileName 文件名称
* @return
*/
public static String getTextFileContent(ChannelSftp sftp, String filePath, String fileName) {
// 目录 及 文件名称
String path = filePath + fileName;
log.error("getTextFileContent. path:{}", path);
//限制文件类型
List<String> fileTypeStrList = Lists.newArrayList("txt", "html");
String[] split = fileName.split("\\.");
String fileTypeStr = split[split.length - 1];
if (!fileTypeStrList.contains(fileTypeStr)) {
log.error("getTextFileContent fileTypeStr is not contains. path:{}", path);
return StringUtils.EMPTY;
}
StringBuilder stringBuilder = new StringBuilder();
try {
// 判断查看目录是否存在
if (!isDirExist(sftp, filePath)) {
log.error("getTextFileContent filePath isDirExist is false. path:{}", path);
return StringUtils.EMPTY;
}
// 获取input stream
InputStream inputStream = sftp.get(path);
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
String line;
BufferedReader br = new BufferedReader(inputStreamReader);
while ((line = br.readLine()) != null) {
stringBuilder.append(line);
}
} catch (SftpException | IOException e) {
log.error("getTextFileContent. e:{}", e.toString());
e.printStackTrace();
}
return stringBuilder.toString();
}
/**
* 判断文件路径在sftp服务器是否存在
*
* @param sftp sftp
* @param directoryPath 文件夹路径 如: roster/test.html
* @return
*/
public static boolean isDirExist(ChannelSftp sftp, String directoryPath) {
if (StringUtils.isEmpty(directoryPath)) {
log.error("isDirExist directoryPath is empty.");
return false;
}
try {
// 判断文件在sftp服务器是否存在
SftpATTRS lstat = sftp.lstat(directoryPath);
System.out.println("is Dir:" + lstat.isDir() + "====:" + directoryPath);
if (lstat.isDir()) {
return true;
}
} catch (SftpException e) {
log.error("isDirExist error. e:{}", e.toString());
e.printStackTrace();
}
return false;
}
/**
* 创建一个文件夹
*
* @param sftp sftp
* @param createPath 创建目录(以 / 结尾)
* @return
*/
public static boolean createDir(ChannelSftp sftp, String createPath) {
// 创建目录不为空
if (StringUtils.isEmpty(createPath)) {
log.error("createDir createPath is empty.");
return false;
}
// 创建目录以 / 结尾
boolean endsWith = createPath.endsWith("/");
if (!endsWith) {
log.error("createDir createPath endsWith is not 【/】.");
return false;
}
// 如果已经存在此目录则直接返回成功
if (isDirExist(sftp, createPath)) {
log.error("createDir createPath isDirExist is true.");
return true;
}
// 创建目录 mkdir
try {
String[] split = createPath.split("/");
StringBuilder sb = new StringBuilder();
for (String path : split) {
sb.append(path).append("/");
String sbPath = sb.toString();
if (!isDirExist(sftp, sbPath)) {
sftp.mkdir(sbPath);
}
}
return true;
} catch (SftpException e) {
log.error("createDir mkdir error. e:{}", e.toString());
e.printStackTrace();
}
return false;
}
/**
* 删除一个文件夹
*
* @param sftp sftp
* @param deletePath 删除目录(以 / 结尾)
* @return
*/
public static boolean deleteDir(ChannelSftp sftp, String deletePath) {
// 删除目录不为空
if (StringUtils.isEmpty(deletePath)) {
log.error("deleteDir deletePath is empty.");
return false;
}
// 删除目录以 / 结尾
boolean endsWith = deletePath.endsWith("/");
if (!endsWith) {
log.error("deleteDir deletePath endsWith is not 【/】.");
return false;
}
// 如果不存在此目录则直接返回成功
if (!isDirExist(sftp, deletePath)) {
log.error("deleteDir deletePath isDirExist is false.");
return true;
}
// 删除
try {
sftp.rmdir(deletePath);
return true;
} catch (SftpException e) {
log.error("deleteDir rm error. e:{}", e.toString());
e.printStackTrace();
}
return false;
}
/**
* 删除文件
*
* @param sftp sftp
* @param filePath /roster
* @param fileName /test.html
* @return
*/
public static boolean deleteFile(ChannelSftp sftp, String filePath, String fileName) {
// 参数校验
if (StringUtils.isEmpty(filePath) || StringUtils.isEmpty(fileName)) {
log.error("deleteFile path is empty. filePath:{}, filePath:{}", filePath, fileName);
return false;
}
// 目录 及 文件名称
String path = filePath + fileName;
log.error("deleteFile path:{}", path);
// 判断目录是否存在
if (!isDirExist(sftp, filePath)) {
log.error("deleteFile filePath isDirExist is false. path:{}", path);
return false;
}
// 删除
try {
sftp.rm(path);
return true;
} catch (SftpException e) {
log.error("deleteFile rm error. e:{}", e.toString());
e.printStackTrace();
}
return false;
}
/**
* 文件上传
*
* @param sftp sftp
* @param fileStream 文件输入流
* @param uploadFilePath 上传文件夹路径
* @param uploadFileName 上传文件名称
* @return
*/
public static boolean uploadFile(ChannelSftp sftp, InputStream fileStream, String uploadFilePath, String uploadFileName) {
// 参数校验
if (Objects.isNull(fileStream) || StringUtils.isEmpty(uploadFilePath) || StringUtils.isEmpty(uploadFileName)) {
log.error("uploadFile data is empty. fileStream:{}, uploadFilePath:{}, uploadFileName:{}", fileStream, uploadFilePath, uploadFileName);
return false;
}
// 目录 及 文件名称
String path = uploadFilePath + uploadFileName;
log.error("uploadFile uploadFilePath:{}", path);
// 判断上传目录是否存在
if (!isDirExist(sftp, uploadFilePath)) {
log.error("uploadFile uploadFilePath isDirExist is false. path:{}", path);
return false;
}
// 上传
try {
sftp.put(fileStream, path);
return true;
} catch (SftpException e) {
log.error("uploadFile put error. e:{}", e.toString());
e.printStackTrace();
}
return false;
}
}
四、测试功能
登录sftp服务器
我们使用xshell登录仅sftp服务器
进入到roster目录下
目前只有roster目录,crisis/notification/email/ 这三层文件夹目录均不存在。
测试创建文件夹
我们执行创建文件夹【roster/crisis/notification/email/】
// 创建文件夹
boolean dir = SftpUtils.createDir(sftp, "roster/crisis/notification/email/");
我们可以看到 依层级关系将目录结构文件夹均创建好了
测试文件上传
我们本地有一个1.html文件 我们试着将其上传到 sftp服务器 刚刚创建的目录上去
// 上传一个文件
File file = new File("C:\\Users\\Administrator\\Desktop\\邮件模板\\1.html");
FileInputStream fileInputStream = new FileInputStream(file);
boolean uploadFile = SftpUtils.uploadFile(sftp, fileInputStream, "roster/crisis/notification/email/", file.getName());
执行成功后,可以看到1.html 文件被成功上传到了sftp服务器的roster/crisis/notification/email/目录下
测试文件读取
我们执行如下读取
// 获取指定file文本
String fileContent = SftpUtils.getTextFileContent(sftp, "roster/crisis/notification/email/", "1.html");
System.out.println(fileContent);
查看控制台,打印1.html 内容如下