1、引入pom.xml配置:
Maven依赖
<!-- SFTP -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
<!-- SSH -->
<dependency>
<groupId>ch.ethz.ganymed</groupId>
<artifactId>ganymed-ssh2</artifactId>
<version>build210</version>
</dependency>
2、SFTP工具类:
SftpUtil
package com.demo.utils;
import com.jcraft.jsch.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
import java.util.Properties;
/**
* @Author zf
* @ClassName SftpUtil.java
* @ProjectName Demo
*/
public class SftpUtil {
private SftpUtil() {
}
/**
* 每个目录下最大子文件(夹)数量
*/
private static final int MAX_CHILD_FILE_NUMBER = 1000;
private static volatile ChannelSftp sftp = null;
private static volatile Session sshSession = null;
/**
* @Description 附件上传
* @param sftpConfig 配置信息
* @param fileName 文件名
* @param inputStream 文件流
* @return
* @throws SftpException
* @throws JSchException
*/
public static String upload(SftpConfig sftpConfig, String fileName, InputStream inputStream) throws SftpException, JSchException {
return upload(sftpConfig,null, fileName, inputStream);
}
/**
* @Description 文件上传
* @param sftpConfig 配置信息
* @param relativePath 文件保存的相对路径(最后一级目录)
* @param fileName 文件名
* @param inputStream 文件流
* @return
* @throws JSchException
* @throws SftpException
*/
public static String upload(SftpConfig sftpConfig, String relativePath, String fileName, InputStream inputStream)
throws JSchException, SftpException {
init(sftpConfig);
createFolder(sftpConfig.getPath());
String filePath = sftpConfig.getPath();
if (relativePath != null && !relativePath.trim().isEmpty()) {
filePath = sftpConfig.getPath() + "/" + relativePath;
}
filePath = generateValidPath(filePath);
filePath = filePath + "/" + fileName;
sftp.put(inputStream, filePath);
return filePath.substring(sftpConfig.getPath().length() + 1);
}
/**
* @Description 文件下载
* @param sftpConfig 配置信息
* @param fileName 文件名
* @return
* @throws JSchException
* @throws SftpException
*/
public static byte[] download(SftpConfig sftpConfig, String fileName) throws JSchException, SftpException,
IOException {
init(sftpConfig);
InputStream inputStream = sftp.get(sftpConfig.getPath() + "/" + fileName);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int n;
byte[] data = new byte[1024];
while ((n = inputStream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, n);
}
buffer.flush();
return buffer.toByteArray();
}
/**
* @Description 判断是否关闭
* @return
*/
public static boolean isClosed() {
if (Objects.isNull(sshSession) || Objects.isNull(sftp)) {
return false;
}
if (Objects.nonNull(sshSession) && sshSession.isConnected() && Objects.nonNull(sftp) && sftp.isConnected()) {
return true;
}
return false;
}
/**
* @Description 关闭
*/
public static void close(){
if (sshSession != null && sshSession.isConnected()) {
sshSession.disconnect();
}
if (sftp != null && sftp.isConnected()) {
sftp.disconnect();
}
}
/**
* @Description 初始化
* @throws JSchException
*/
private static void init(SftpConfig sftpConfig) throws JSchException {
if (!isClosed()) {
synchronized (SftpUtil.class) {
if (!isClosed()) {
JSch jsch = new JSch();
jsch.getSession(sftpConfig.getUsername(), sftpConfig.getHost(), sftpConfig.getPort());
sshSession = jsch.getSession(sftpConfig.getUsername(), sftpConfig.getHost(), sftpConfig.getPort());
sshSession.setPassword(sftpConfig.getPassword());
Properties sshConfig = new Properties();
sshConfig.put("StrictHostKeyChecking", "no");
sshSession.setConfig(sshConfig);
sshSession.connect();
Channel channel = sshSession.openChannel("sftp");
channel.connect();
sftp = (ChannelSftp) channel;
}
}
}
}
/**
* @Description 创建目录
* @param path 目录路径
* @return
*/
private static boolean createFolder(String path) {
try {
sftp.mkdir(path);
} catch (SftpException e) {
return false;
}
return true;
}
/**
* @Description 删除指定文件
* @param sftpConfig 配置信息
* @param fileName 文件名
* @return boolean 删除结果
*/
public static boolean rm(SftpConfig sftpConfig, String fileName) throws JSchException {
init(sftpConfig);
Boolean result = false;
try {
sftp.cd(sftpConfig.getPath());
sftp.rm(fileName);
result = true;
} catch (Exception e) {
e.printStackTrace();
result = false;
}
return result;
}
/**
* @Description 统计目录下文件(夹)数量
* @param path 目录路径
* @return
*/
private static int countFiles(String path) throws SftpException {
sftp.cd(path);
return sftp.ls(path).size();
}
/**
* @Description 校验路径是否可用
* @param path 目录路径
* @return
*/
private static boolean validatePathValid(String path) {
int countFiles = 0;
try {
countFiles = countFiles(path);
} catch (SftpException e) {
createFolder(path);
}
if (countFiles <= MAX_CHILD_FILE_NUMBER) {
return true;
}
return false;
}
/**
* @Description 校验文件是否存在
* @param sftpConfig 配置信息
* @param fileName 文件名
* @return 1.文件已存在、-1.文件不存在
*/
public static int validateFileValid(SftpConfig sftpConfig, String fileName) throws JSchException {
init(sftpConfig);
try {
sftp.get(sftpConfig.getPath() + fileName);
return 1;
}catch (Exception e){
//文件不存在
if ("No such file".equals(e.getMessage())){
return -1;
}
}
return 0;
}
/**
* @Description 生成有效路径
* @param path 目录路径
* @return
*/
private static String generateValidPath(String path) {
if (validatePathValid(path)) {
return path;
} else {
String newPath = path + String.valueOf(System.currentTimeMillis()).substring(9);
createFolder(newPath);
return newPath;
}
}
/**
* 配置信息
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SftpConfig {
/**
* 主机地址
*/
private String host;
/**
* sftp 连接端口
*/
private int port;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 文件保存根路径
*/
private String path;
}
public static SftpConfig sftpConfig() {
return new SftpUtil().new SftpConfig();
}
/**
* @Description 配置信息
* @param host 主机地址
* @param port 主机端口
* @param username 用户名
* @param password 密码
* @param path 目录路径
* @return 配置信息
*/
public static SftpConfig sftpConfig(String host, int port, String username, String password, String path) {
return new SftpUtil().new SftpConfig(host, port, username, password, path);
}
}
3、Compress工具类:
CompressUtil
package com.demo.utils;
import java.io.IOException;
import java.io.PrintWriter;
import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.demo.config.SftpUploadConfig;
/**
* @Author zf
* @Description 通过名称远程压缩指定目录下的文件
* @ClassName CompressUtil.java
* @ProjectName Demo
*/
public class CompressUtil {
/*
* @param fileName 文件名称
*/
public static Boolean remoteZipToFile(String fileName, SftpUploadConfig uploadConfig) {
Boolean result = false;
try {
SftpUtil.SftpConfig sftpConfig = SftpUtil.sftpConfig();
BeanUtil.copyProperties(uploadConfig, sftpConfig, CopyOptions.create().ignoreNullValue().ignoreError());
Connection connection = new Connection(sftpConfig.getHost());// 创建一个连接实例
connection.connect();// Now connect
//连接状态
boolean isAuthenticated = connection.authenticateWithPassword(sftpConfig.getUsername(),
sftpConfig.getPassword());// Authenticate
if (isAuthenticated == false)
throw new IOException("user and password error");
Session sess = connection.openSession();// Create a session
System.out.println("start exec command.......");
sess.requestPTY("bash");
sess.startShell();
PrintWriter out = new PrintWriter(sess.getStdin());
out.println("cd /" + sftpConfig.getPath());
out.println("ll");
out.println("zip -r " + fileName + ".zip " + fileName + "-*"); //压缩文件
out.println("ll");
out.println("exit");
out.close();
System.out.println("ExitCode: " + sess.getExitStatus());
sess.close();/* Close this session */
connection.close();/* Close the connection */
result = true;
} catch (IOException e) {
e.printStackTrace(System.err);
result =false;
System.exit(2);
}
return result;
}
}
4、配置类:
SftpUploadConfig
package com.demo.config;
import lombok.Getter;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
* @Author zf
* @ClassName SftpUploadConfig.java
* @ProjectName Demo
*/
@ToString
@Getter
@Configuration
public class SftpUploadConfig {
/**
* 主机地址
*/
@Value("${uploadSftp.host}")
private String host;
/**
* sftp 连接端口
*/
@Value("${uploadSftp.port}")
private int port;
/**
* 用户名
*/
@Value("${uploadSftp.username}")
private String username;
/**
* 密码
*/
@Value("${uploadSftp.password}")
private String password;
/**
* 文件保存路径
*/
@Value("${uploadSftp.path}")
private String path;
}
5、application.yml:
application.yml
uploadSftp:
host:
port:
username:
password:
path:
6、Service:
Service
@Resource
private SftpUploadConfig uploadConfig;
/**
* @Description 下载文件
* @param month 月份(例如:2022-06)
*/
@SneakyThrows
public ResponseEntity<byte[]> downFile(String month) {
String fileName = month + ".zip";
SftpUtil.SftpConfig sftpConfig = SftpUtil.sftpConfig();
BeanUtil.copyProperties(uploadConfig, sftpConfig, CopyOptions.create().ignoreNullValue().ignoreError());
//如果压缩包已存在,则删除历史文件。
if (SftpUtil.validateFileValid(sftpConfig, fileName) == 1) {
SftpUtil.rm(sftpConfig, fileName);
}
//重新执行压缩流程。
Boolean is = CompressUtil.remoteZipToFile(month, uploadConfig);
//如果压缩完成,则下载文件。
if (is){
byte[] bytes = SftpUtil.download(sftpConfig, fileName);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType("application", "zip"));
headers.setContentDispositionFormData("attachment", fileName);
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
}
return null;
}
7、Controller:
Controller
@ApiOperation(value = "文件(下载)")
@GetMapping(value = "/downFile", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
@SneakyThrows
public ResponseEntity<byte[]> downFile (String month) {
ResponseEntity<byte[]> responseEntity = downServer.downFile(month);
return responseEntity;
}