1、使用jsch
<!--sftp文件上传-->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
2、配置类
package com.base.jsch;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Data
@Component
@ConfigurationProperties(prefix = "sftp")
public class SFTPConfig {
/**
* SFTP 服务器地址IP地址
*/
@NotBlank(message = "IP地址不能为空")
private String ip;
/**
* SFTP 端口
*/
@NotNull(message = "端口不能为空")
private int port;
/**
* SFTP 登录用户名
*/
@NotBlank(message = "用户名不能为空")
private String username;
/**
* SFTP 登录密码
*/
@NotBlank(message = "密码不能为空")
private String password;
/**
* SFTP 私钥
*/
private String privateKey;
/**
* 上传失败隔多长时间重新上传
*/
@NotNull(message = "上传失败重试间隔不能为空")
private int uploadSleep;
/**
* 重新上传的次数
*/
@NotNull(message = "重新上传次数不能为空")
private int uploadRetry;
/**
* 服务器路径
*/
@NotBlank(message = "服务器路径不能为空")
private String serverStorageDir;
@NotNull(message = "文件名是否需要中文编码")
private Boolean isFileNameGBK;
}
3、获取连接
package com.base.jsch;
import java.lang.reflect.Field;
import java.util.Properties;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* SFTP工厂类,用于获取SFTP的连接和关闭SFTP的连接
*/
@Slf4j
@Component
public class SFTPConnection {
@Autowired
private SFTPConfig sftpConfig;
private static ChannelSftp client;
private static Session session;
/**
* 建立连接
*
* @return client
*/
public synchronized ChannelSftp makeConnection() {
if (client == null || session == null || !client.isConnected() || !session.isConnected()) {
try {
JSch jsch = new JSch();
if (sftpConfig.getPrivateKey() != null) {
jsch.addIdentity(sftpConfig.getPrivateKey());// 设置私钥
}
session = jsch.getSession(sftpConfig.getUsername(), sftpConfig.getIp(), sftpConfig.getPort());
if (sftpConfig.getPassword() != null) {
session.setPassword(sftpConfig.getPassword());
}
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
client = (ChannelSftp)session.openChannel("sftp");
client.connect();
log.info("sftp服务器连接成功");
// 文件名中文乱码
if(Boolean.TRUE.equals(sftpConfig.getIsFileNameGBK())){
try{
Class<ChannelSftp> cl = ChannelSftp.class;
Field f1 =cl.getDeclaredField("server_version");
f1.setAccessible(true);
f1.set(client, 2);
client.setFilenameEncoding("gbk");
log.info("设置中文编码");
}catch (NoSuchFieldException | SftpException | IllegalAccessException e) {
log.error("设置中文编码" + e.getMessage());
throw new RuntimeException(e);
}
}
} catch (JSchException e) {
log.error("主机sftp登录失败,检测登录ip,端口号,用户名密码是否正确,错误信息为" + e.getMessage());
}
}
return client;
}
/**
* 关闭连接
*/
public void logout() {
if (client != null) {
if (client.isConnected()) {
client.disconnect();
}
}
if (session != null) {
if (session.isConnected()) {
session.disconnect();
}
}
}
}
4、上传工具类
package com.base.jsch;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.SftpException;
/**
* 工具类
*/
@Slf4j
@Component
public class SFTPClientUtils {
@Autowired
private SFTPConfig sftpConfig;
@Autowired
private SFTPConnection sftpConnection;
public synchronized boolean upload(InputStream fileIs, String fileName) {
boolean result = false;
int i = 0;
while (!result) {
log.info("*****开始登陆*****");
ChannelSftp sftp = sftpConnection.makeConnection();
if(null == sftp || !sftp.isConnected() || sftp.isClosed()){
log.info("连接无效");
return result;
}
log.info("******登陆成功*****");
try {
sftp.cd(sftpConfig.getServerStorageDir());
} catch (SftpException e) {
if(sftp.isConnected()){
log.info("sftp文件上传,目录不存在开始创建");
try {
sftp.mkdir(sftpConfig.getServerStorageDir());
sftp.cd(sftpConfig.getServerStorageDir());
} catch (SftpException e1) {
log.info("sftp文件上传,目录创建失败,错误信息:" + e1.fillInStackTrace());
}
}else{
log.info("sftp连接已经失效");
return result;
}
}
try {
sftp.put(fileIs, fileName, new FileProgressMonitor(fileIs.available()), ChannelSftp.OVERWRITE);
// 用下面的方法不会调用进度监控
//sftp.put(fileIs, fileName);
if (i > 0) {
log.info("sftp重试文件上传成功,ftp路径:" + sftpConfig.getServerStorageDir() + ",文件名称:" + fileName);
} else {
log.info("sftp文件上传成功,ftp路径为" + sftpConfig.getServerStorageDir() + ",文件名称:" + fileName);
}
result = true;
} catch (Exception e) {
i++;
log.error("sftp文件上传失败,重试中。。。第" + i + "次,错误信息" + e.fillInStackTrace());
if (i > sftpConfig.getUploadRetry()) {
sftpConnection.logout();
log.error("sftp文件上传失败,超过重试次数结束重试,错误信息" + e.fillInStackTrace());
return result;
}
try {
TimeUnit.MILLISECONDS.sleep(sftpConfig.getUploadSleep());
} catch (InterruptedException e1) {
sftpConnection.logout();
e1.printStackTrace();
log.error("sftp文件上传失败,系统检测出异常:" + e);
}
}
}
sftpConnection.logout();
log.info("sftp上传:" + result);
try {
fileIs.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
return result;
}
public synchronized void download(String directory, String downloadFile, String localDir) {
log.info("*****开始登陆*****");
ChannelSftp sftp = sftpConnection.makeConnection();
log.info("******登陆成功*****");
if (directory != null && !"".equals(directory)) {
try {
sftp.cd(directory);
} catch (SftpException e) {
log.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();
}
sftpConnection.logout();
}
// /**
// * 文件上传 将文件对象上传到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) {
// log.info("*****开始登陆*****");
// ChannelSftp sftp = SFTPConnectionFactory.makeConnection();
// log.info("******登陆成功*****");
// try {
// sftp.cd(basePath);
// sftp.cd(directory);
// } catch (SftpException e) {
// log.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) {
// log.info("sftp文件上传,目录创建失败,错误信息:" + e1.fillInStackTrace());
// }
// }
// }
// }
// try {
// sftp.put(fileIs, fileName, new FileProgressMonitor(fileIs.available()), ChannelSftp.OVERWRITE);
// // 用下面的方法不会调用进度监控
// //sftp.put(fileIs, fileName);
// if (i > 0) {
// log.info("sftp重试文件上传成功,ftp路径:" + basePath + directory + ",文件名称:" + fileName);
// } else {
// log.info("sftp文件上传成功,ftp路径为" + basePath + directory + ",文件名称:" + fileName);
// }
// result = true;
// } catch (Exception e) {
// i++;
// log.error("sftp文件上传失败,重试中。。。第" + i + "次,错误信息" + e.fillInStackTrace());
// if (i > uploadRettry) {
// SFTPConnectionFactory.logout();
// log.error("sftp文件上传失败,超过重试次数结束重试,错误信息" + e.fillInStackTrace());
// return result;
// }
// try {
// TimeUnit.MILLISECONDS.sleep(uploadSleep);
// } catch (InterruptedException e1) {
// SFTPConnectionFactory.logout();
// e1.printStackTrace();
// log.error("sftp文件上传失败,系统检测出异常:" + e);
// }
// }
//
// }
// SFTPConnectionFactory.logout();
// log.info("sftp上传:" + result);
// try {
// fileIs.close();
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
// return result;
// }
// /**
// * 下载文件至本地
// *
// * @param directory 要下载文件所在目录,例如 /home
// * @param downloadFile 要下载的文件,例如 码海无际.txt
// * @param localDir 本地路径,例如 D:\\home
// * @return
// */
// public synchronized static void download(String directory, String downloadFile, String localDir) {
// log.info("*****开始登陆*****");
// ChannelSftp sftp = SFTPConnectionFactory.makeConnection();
// log.info("******登陆成功*****");
// if (directory != null && !"".equals(directory)) {
// try {
// sftp.cd(directory);
// } catch (SftpException e) {
// log.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 boolean delete(String directory, String deleteFile) {
boolean result = false;
ChannelSftp sftp = sftpConnection.makeConnection();
try {
sftp.cd(directory);
sftp.rm(deleteFile);
} catch (SftpException e) {
e.printStackTrace();
}
result = true;
sftpConnection.logout();
return result;
}
}
5、上传进度
package com.base.jsch;
import com.jcraft.jsch.SftpProgressMonitor;
import lombok.extern.slf4j.Slf4j;
import java.text.DecimalFormat;
@Slf4j
public class FileProgressMonitor implements SftpProgressMonitor {
private long fileSize; // 记录文件总大小
private long transfered; // 记录已传输的数据总大小
public FileProgressMonitor(long fileSize) {
if (fileSize != 0) {
this.fileSize = fileSize;
log.info("数据大小: " + fileSize + " bytes");
} else {
log.info("文件错误");
}
}
@Override
public void init(int op, String src, String dest, long max) {
log.info("开始");
}
/**
* 实现了SftpProgressMonitor接口的count方法
*/
@Override
public boolean count(long count) {
add(count);
return true;
}
/**
* 实现了SftpProgressMonitor接口的end方法
*/
@Override
public void end() {
log.info("结束");
}
private synchronized void add(long count) {
transfered = transfered + count;
log.info("已传输数据大小: " + transfered + " bytes");
double d = ((double) transfered * 100) / (double) fileSize;
DecimalFormat df = new DecimalFormat("#.##");
log.info("已传输数据占比: " + df.format(d) + "%");
}
}
6、配置文件
sftp:
ip: 192.168.1.111
port: 22
username: root
password: root
uploadSleep: 1000 #上传失败隔1秒重新上传
uploadRettry: 1 #重新上传的次数
serverStorageDir: /opt/zzz #服务器路径
isFileNameGBK: true #文件名是否gbk编码
7、上传测试
String src = "D:\\home\\test.xlsx"; // 本地文件名
File file = new File(src);
try (FileInputStream fis = new FileInputStream(file)) {
boolean upload = sftpClientUtils.upload(fis, ele.getName());
} catch (IOException e) {
e.printStackTrace();
}