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 {@Autowiredprivate 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 {@Autowiredprivate SFTPConfig sftpConfig;@Autowiredprivate 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("文件错误");}}@Overridepublic void init(int op, String src, String dest, long max) {log.info("开始");}/*** 实现了SftpProgressMonitor接口的count方法*/@Overridepublic boolean count(long count) {add(count);return true;}/*** 实现了SftpProgressMonitor接口的end方法*/@Overridepublic 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.111port: 22username: rootpassword: rootuploadSleep: 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();
}