与其抱怨过去,不如认真面对未来。
1.环境:
JDK1.8+SpringBoot2.3.3
2.引入依赖
org.springframework.boot
spring-boot-starter
com.jcraft
jsch
0.1.54
org.springframework.boot
spring-boot-starter-logging
org.springframework.boot
spring-boot-configuration-processor
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
3.启动类
package com.mahaiwuji;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class FileSocketApplication {
public static void main(String[] args) {
SpringApplication.run(FileSocketApplication.class, args);
}
}
4.配置文件application.yml
sftp:
ip: 192.168.100.110
port: 22
username: root
password: 'root'
uploadSleep: 1000 #上传失败隔1秒重新上传
uploadRettry: 10 #重新上传的次数
5.SFTP工厂类,用于获取SFTP的连接和关闭SFTP的连接
package com.mahaiwuji.sftp;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
/**
* SFTP工厂类,用于获取SFTP的连接和关闭SFTP的连接
*/
@Component
@ConfigurationProperties(prefix = "sftp")
public class SFTPConnectionFactory {
private static Logger logger = LoggerFactory.getLogger(SFTPConnectionFactory.class);
/**
* SFTP 服务器地址IP地址
*/
private static String ip;
/**
* SFTP 端口
*/
private static int port;
/**
* SFTP 登录用户名
*/
private static String username;
/**
* SFTP 登录密码
*/
private static String password;
/**
* 私钥
*/
private static String privateKey;
private static ChannelSftp client;
private static Session session;
private SFTPConnectionFactory() {
}
/**
* 建立连接
*
* @return
*/
public synchronized static ChannelSftp makeConnection() {
if (client == null || session == null || !client.isConnected() || !session.isConnected()) {
try {
JSch jsch = new JSch();
if (privateKey != null) {
jsch.addIdentity(privateKey);// 设置私钥
}
session = jsch.getSession(username, ip, port);
if (password != null) {
session.setPassword(password);
}
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
Channel channel = session.openChannel("sftp");
channel.connect();
client = (ChannelSftp) channel;
logger.info("sftp服务器连接成功");
} catch (JSchException e) {
logger.error("主机sftp登录失败,检测登录ip,端口号,用户名密码是否正确,错误信息为" + e.getMessage());
}
}
return client;
}
/**
* 关闭连接
*/
public static void logout() {
if (client != null) {
if (client.isConnected()) {
client.disconnect();
}
}
if (session != null) {
if (session.isConnected()) {
session.disconnect();
}
}
}
public static String getIp() {
return ip;
}
public void setIp(String ip) {
SFTPConnectionFactory.ip = ip;
}
public static int getPort() {
return port;
}
public void setPort(int port) {
SFTPConnectionFactory.port = port;
}
public static String getUsername() {
return username;
}
public void setUsername(String username) {
SFTPConnectionFactory.username = username;
}
public static String getPassword() {
return password;
}
public void setPassword(String password) {
SFTPConnectionFactory.password = password;
}
public static String getPrivateKey() {
return privateKey;
}
public void setPrivateKey(String privateKey) {
SFTPConnectionFactory.privateKey = privateKey;
}
}
6.进度监听器
package com.mahaiwuji.sftp;
import java.text.DecimalFormat;
import com.jcraft.jsch.SftpProgressMonitor;
/**
* 进度监听器
*/
public class FileProgressMonitor implements SftpProgressMonitor {
private long fileSize; // 记录文件总大小
private long transfered; // 记录已传输的数据总大小
public FileProgressMonitor(long fileSize) {
if (fileSize != 0) {
this.fileSize = fileSize;
System.out.println("数据大小: " + fileSize + " bytes");
} else {
System.out.println("文件错误");
}
}
@Override
public void init(int op, String src, String dest, long max) {
System.out.println("开始");
}
/**
* 实现了SftpProgressMonitor接口的count方法
*/
@Override
public boolean count(long count) {
add(count);
return true;
}
/**
* 实现了SftpProgressMonitor接口的end方法
*/
@Override
public void end() {
System.out.println("结束");
}
private synchronized void add(long count) {
transfered = transfered + count;
System.out.println("已传输数据大小: " + transfered + " bytes");
double d = ((double) transfered * 100) / (double) fileSize;
DecimalFormat df = new DecimalFormat("#.##");
System.out.println("已传输数据占比: " + df.format(d) + "%");
}
}
7.上传下载工具类
package com.mahaiwuji.sftp;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;
import com.jcraft.jsch.SftpATTRS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.SftpException;
/**
* 工具类
*/
@Component
@ConfigurationProperties(prefix = "sftp")
public class SFTPClientUtils {
private static int uploadSleep;
private static int uploadRettry;
private static Logger logger = LoggerFactory.getLogger(SFTPClientUtils.class);
private static String tempFilePath;
/**
* 文件上传 将文件对象上传到sftp作为文件。文件完整路径=basePath+directory 目录不存在则会上传文件夹
*
* @param basePath 服务器的基础路径,服务器上必须有该目录,例如 /home
* @param directory 上传到的目录,服务器上没有会自动创建,有则不创建,例如 /test
* @param fileIs 文件流
* @param fileName 放到服务器上要保存的名字,例如 码海无际.txt
* @return
*/
public synchronized static boolean upload(String basePath, String directory, InputStream fileIs, String fileName) {
boolean result = false;
Integer i = 0;
while (!result) {
logger.info("*****开始登陆*****");
ChannelSftp sftp = SFTPConnectionFactory.makeConnection();
logger.info("******登陆成功*****");
try {
sftp.cd(basePath);
sftp.cd(directory);
} catch (SftpException e) {
logger.info("sftp文件上传,目录不存在开始创建");
String[] dirs = directory.split("/");
String tempPath = basePath;
for (String dir : dirs) {
if (null == dir || "".equals(dir)) {
continue;
}
tempPath += "/" + dir;
try {
sftp.cd(tempPath);
} catch (SftpException ex) {
try {
sftp.mkdir(tempPath);
sftp.cd(tempPath);
} catch (SftpException e1) {
logger.info("sftp文件上传,目录创建失败,错误信息:" + e1.fillInStackTrace());
}
}
}
}
try {
sftp.put(fileIs, fileName, new FileProgressMonitor(fileIs.available()), ChannelSftp.OVERWRITE);
// 用下面的方法不会调用进度监控
//sftp.put(fileIs, fileName);
if (i > 0) {
logger.info("sftp重试文件上传成功,ftp路径:" + basePath + directory + ",文件名称:" + fileName);
} else {
logger.info("sftp文件上传成功,ftp路径为" + basePath + directory + ",文件名称:" + fileName);
}
result = true;
} catch (Exception e) {
i++;
logger.error("sftp文件上传失败,重试中。。。第" + i + "次,错误信息" + e.fillInStackTrace());
if (i > uploadRettry) {
SFTPConnectionFactory.logout();
logger.error("sftp文件上传失败,超过重试次数结束重试,错误信息" + e.fillInStackTrace());
return result;
}
try {
TimeUnit.MILLISECONDS.sleep(uploadSleep);
} catch (InterruptedException e1) {
SFTPConnectionFactory.logout();
e1.printStackTrace();
logger.error("sftp文件上传失败,系统检测出异常:" + e);
}
}
}
SFTPConnectionFactory.logout();
logger.info("sftp上传:" + result);
return result;
}
/**
* 下载文件至本地
*
* @param directory 要下载文件所在目录,例如 /home
* @param downloadFile 要下载的文件,例如 码海无际.txt
* @param localDir 本地路径,例如 D:\\home
* @return
*/
public synchronized static void download(String directory, String downloadFile, String localDir) {
logger.info("*****开始登陆*****");
ChannelSftp sftp = SFTPConnectionFactory.makeConnection();
logger.info("******登陆成功*****");
if (directory != null && !"".equals(directory)) {
try {
sftp.cd(directory);
} catch (SftpException e) {
logger.error("sftp文件下载,目录不存在,错误信息" + e.fillInStackTrace());
}
}
String src = directory + "/" + downloadFile;
try {
SftpATTRS attr = sftp.stat(src);
long fileSize = attr.getSize();
InputStream inputStream = sftp.get(src, new FileProgressMonitor(fileSize));
// 用下面的方法不会调用进度监控
//InputStream inputStream = sftp.get(src);
String tempFile = localDir + "/" + downloadFile;
File file = new File(tempFile);
File fileParent = file.getParentFile();
if (!fileParent.exists()) {
fileParent.mkdirs();
}
if (!file.exists()) {
writeToLocal(tempFile, inputStream);
}
} catch (SftpException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
SFTPConnectionFactory.logout();
}
/**
* 将InputStream写入本地文件
*
* @param destination 写入本地目录
* @param input 输入流
* @throws IOException
*/
public static void writeToLocal(String destination, InputStream input) throws IOException {
int index;
byte[] bytes = new byte[1024];
FileOutputStream downloadFile = new FileOutputStream(destination);
while ((index = input.read(bytes)) != -1) {
downloadFile.write(bytes, 0, index);
downloadFile.flush();
}
downloadFile.close();
input.close();
}
/**
* 删除文件
*
* @param directory 要删除文件所在目录,例如 /home
* @param deleteFile 要删除的文件,例如 码海无际.txt
*/
public synchronized static boolean delete(String directory, String deleteFile) {
boolean result = false;
ChannelSftp sftp = SFTPConnectionFactory.makeConnection();
try {
sftp.cd(directory);
sftp.rm(deleteFile);
} catch (SftpException e) {
e.printStackTrace();
}
result = true;
SFTPConnectionFactory.logout();
return result;
}
public static int getUploadSleep() {
return uploadSleep;
}
public void setUploadSleep(int uploadSleep) {
SFTPClientUtils.uploadSleep = uploadSleep;
}
public static int getUploadRettry() {
return uploadRettry;
}
public void setUploadRettry(int uploadRettry) {
SFTPClientUtils.uploadRettry = uploadRettry;
}
public static String getTempFilePath() {
return tempFilePath;
}
public void setTempFilePath(String tempFilePath) {
SFTPClientUtils.tempFilePath = tempFilePath;
}
}
8.测试类
package com.mahaiwuji.file;
import com.mahaiwuji.sftp.SFTPClientUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.*;
@SpringBootTest
class FileTest {
/**
* 上传
*
* @throws FileNotFoundException
*/
@Test
void uploadTest() throws FileNotFoundException {
String src = "D:\\home\\360zip_setup_4.0.0.1200.exe"; // 本地文件名
File file = new File(src);
SFTPClientUtils.upload("/home", "/aa", new FileInputStream(file), "360zip_setup_4.0.0.1200.exe");
}
/**
* 下载
*/
@Test
void downloadTest() {
SFTPClientUtils.download("/home/aa", "360zip_setup_4.0.0.1200.exe", "D:\\home");
}
/**
* 删除
*/
@Test
void deleteTest() {
SFTPClientUtils.delete("/home/aa", "360zip_setup_4.0.0.1200.exe");
}
}
9.参考文献
https://www.cnblogs.com/longyg/archive/2012/06/25/2556576.html
https://www.cnblogs.com/longyg/archive/2012/06/25/2561332.html