SFTP的应用
封装utils,用SFTP方式上传/下载图像
maven
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.jcraft/jsch -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.ant/ant -->
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.10.10</version>
</dependency>
utils
package com.wh.sftp;
import com.jcraft.jsch.*;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import org.apache.log4j.Logger;
import org.apache.poi.util.IOUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
public class SFTPUtil {
private ThreadLocal<Scope> threadLocal = new ThreadLocal<>();
private ChannelSftp sftp;
private Session session;
/**
* FTP 登录用户名
*/
private String username;
/**
* FTP 登录密码
*/
private String password;
/**
* 私钥
*/
private String privateKey;
/**
* FTP 服务器地址IP地址
*/
private String host;
/**
* FTP 端口
*/
private int port;
private String hostDir;
private static Logger log = Logger.getLogger(SFTPUtil.class);
/**
* 构造基于密码认证的sftp对象
*
* @param username
* @param password
* @param host
* @param port
*/
public SFTPUtil(String username, String password, String host, int port, String hostDir) {
this.username = username;
this.password = password;
this.host = host;
this.port = port;
this.hostDir = hostDir;
}
public SFTPUtil() {
// String username = RedisUtils.getDictValue("SFTP_MEMBER", "SFTP_NAME");
// String password = RedisUtils.getDictValue("SFTP_MEMBER", "SFTP_PASSWORD");
// String host = RedisUtils.getDictValue("SFTP_MEMBER", "SFTP_HOST");
// int port = Integer.parseInt(RedisUtils.getDictValue("SFTP_MEMBER", "SFTP_PORT"));
// String hostDir = RedisUtils.getDictValue("SFTP_MEMBER", "SFTP_PATH");
// String username = "sftp";
// String password = "2020@newImage";
// String host = "49.232.18.205";
// int port = 22;
// String hostDir = "/upload";
this.username = "ergo_life_dev";
this.password = "ergo_life_dev";
this.host = "49.232.76.227";
this.port = 22;
this.hostDir = "/upload";
}
/**
* 连接sftp服务器
*
* @throws Exception
*/
public void login() {
try {
JSch jsch = new JSch();
if (privateKey != null) {
jsch.addIdentity(privateKey);// 设置私钥
log.info("sftp connect,path of private key file:{}");
}
log.info("sftp connect by host:{" + host + "} username:{" + username + "}");
session = jsch.getSession(username, host, port);
log.info("Session is build");
if (password != null) {
session.setPassword(password);
}
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
log.info(session.toString());
session.connect();
log.info("Session is connected");
Channel channel = session.openChannel("sftp");
channel.connect();
log.info("channel is connected");
sftp = (ChannelSftp) channel;
log.info(String.format("sftp server host:[%s] port:[%s] is connect successfull", host, port));
} catch (JSchException e) {
e.printStackTrace();
log.error("SFTP连接失败", e);
}
}
/**
* 关闭连接 server
*/
public void logout() {
if (sftp != null) {
if (sftp.isConnected()) {
sftp.disconnect();
log.info("sftp is closed already");
}
}
if (session != null) {
if (session.isConnected()) {
session.disconnect();
log.info("sshSession is closed already");
}
}
}
public void cd(String directory) throws SftpException {
sftp.cd(directory);
}
public Vector ls(String path) throws SftpException {
return sftp.ls(path);
}
public String pwd() throws Exception {
String pwd = sftp.pwd();
return pwd;
}
/**
* 将输入流的数据上传到sftp作为文件
*
* @param directory 上传到该目录
* @param sftpFileName sftp端文件名
* @param input 输入流
* @throws SftpException
* @throws Exception
*/
public void upload(String directory, String sftpFileName, InputStream input) throws SftpException {
try {
sftp.cd(directory);
} catch (SftpException e) {
log.warn("directory is not exist");
mkdirDirs(directory);
sftp.cd(directory);
}
sftp.put(input, sftpFileName);
log.info("file:{" + sftpFileName + "} is upload successful");
}
/**
* 上传单个文件
*
* @param directory 上传到sftp目录
* @param uploadFile 要上传的文件,包括路径
* @throws FileNotFoundException
* @throws SftpException
* @throws Exception
*/
public void upload(String directory, String uploadFile) throws FileNotFoundException, SftpException {
File file = new File(uploadFile);
upload(directory, file.getName(), new FileInputStream(file));
}
/**
* 将byte[]上传到sftp,作为文件。注意:从String生成byte[]是,要指定字符集。
*
* @param directory 上传到sftp目录
* @param sftpFileName 文件在sftp端的命名
* @param byteArr 要上传的字节数组
* @throws SftpException
* @throws Exception
*/
public void upload(String directory, String sftpFileName, byte[] byteArr) throws SftpException {
upload(directory, sftpFileName, new ByteArrayInputStream(byteArr));
}
/**
* 将字符串按照指定的字符编码上传到sftp
*
* @param directory 上传到sftp目录
* @param sftpFileName 文件在sftp端的命名
* @param dataStr 待上传的数据
* @param charsetName sftp上的文件,按该字符编码保存
* @throws UnsupportedEncodingException
* @throws SftpException
* @throws Exception
*/
public void upload(String directory, String sftpFileName, String dataStr, String charsetName) throws UnsupportedEncodingException, SftpException {
upload(directory, sftpFileName, new ByteArrayInputStream(dataStr.getBytes(charsetName)));
}
/**
* 下载文件
*
* @param directory 下载目录
* @param downloadFile 下载的文件
* @param saveFile 存在本地的路径
* @throws SftpException
* @throws FileNotFoundException
* @throws Exception
*/
public void download(String directory, String downloadFile, String saveFile) throws SftpException, FileNotFoundException {
if (directory != null && !"".equals(directory)) {
log.info("directory:" + directory);
sftp.cd(directory);
}
File file = new File(saveFile + downloadFile);
log.info("downloadFile" + downloadFile);
sftp.get(downloadFile, saveFile);
log.info("file:{" + downloadFile + "} is download successful");
}
/**
* 下载文件
*
* @param directory 下载目录
* @param downloadFile 下载的文件名
* @return 字节数组
* @throws SftpException
* @throws IOException
* @throws Exception
*/
public byte[] download(String directory, String downloadFile) throws SftpException, IOException {
if (directory != null && !"".equals(directory)) {
sftp.cd(directory);
}
InputStream is = sftp.get(downloadFile);
byte[] fileData = IOUtils.toByteArray(is);
log.info("file:{" + downloadFile + "} is download successful");
return fileData;
}
/**
* 下载文件流
*
* @param directory 下载目录
* @param downloadFile 下载的文件名
* @return 字节流
* @throws SftpException
* @throws IOException
* @throws Exception
*/
public InputStream downloadStream(String directory, String downloadFile) throws SftpException, IOException {
if (directory != null && !"".equals(directory)) {
sftp.cd(directory);
}
InputStream is = sftp.get(downloadFile);
return is;
}
public InputStream downloadStream(String downloadFile) throws SftpException, IOException {
InputStream is = sftp.get(downloadFile);
return is;
}
public boolean createFile(InputStream inputStream, String filePath) {
try {
sftp.put(inputStream, filePath);
log.info("创建文件完成");
return true;
} catch (Exception e) {
log.info("创建文件失败");
e.printStackTrace();
return false;
}
}
/**
* 删除文件
*
* @param directory 要删除文件所在目录
* @param deleteFile 要删除的文件
* @throws SftpException
* @throws Exception
*/
public void delete(String directory, String deleteFile) throws SftpException {
sftp.cd(directory);
sftp.rm(deleteFile);
}
public void deleteFile(String deleteFile) {
try {
sftp.rm(deleteFile);
sftp.ls(sftp.pwd());
} catch (SftpException e) {
e.printStackTrace();
}
}
/**
* 列出目录下的文件
*
* @param directory 要列出的目录
* @return
* @throws SftpException
*/
public Vector<LsEntry> listFiles(String directory) throws SftpException {
return sftp.ls(directory);
}
public static Map<String, String> zipPaths(String path) {
//因为这里我的zip文件是存放在707的子目录下面的,所以在这里做了遍历
File file = new File(path + "\\");
String fileName = null;
String filePath = null;
//在这里声明一个Map,将遍历得到的zip文件的名字作为key,zip文件的路径作为value。
Map<String, String> fileMap = new HashMap<String, String>();
if (file.isDirectory()) {
for (File files : file.listFiles()) {
if (files.isDirectory()) {
for (File f : files.listFiles()) {
if (f.isDirectory()) {
for (File ff : f.listFiles()) {
fileName = ff.getName();
filePath = ff.getPath();
fileMap.put(fileName, filePath);
}
} else {
fileName = f.getName();
filePath = f.getPath();
fileMap.put(fileName, filePath);
}
}
} else {
fileName = files.getName();
filePath = files.getPath();
fileMap.put(fileName, filePath);
}
}
}
//最后返回一个map给调用者。
return fileMap;
}
/**
* 解压 zipPath 压缩的zip文件夹,unZipPath 文件夹
*
* @param zipPath
* @param unZipPath
* @return
* @throws Exception
*/
public String unZip(String zipPath, String unZipPath) throws Exception {
String zipFileName = null;
String ZipFilePath = null;
String outputDirectory = null;
//调用上面zipPaths方法,拿到map。
Map<String, String> fileMap = zipPaths(zipPath);
for (Map.Entry<String, String> entry : fileMap.entrySet()) {
//压缩文件的名字
zipFileName = entry.getKey();
//压缩文件的路径
ZipFilePath = entry.getValue();
File file = new File(ZipFilePath);
//得到zip文件的父文件夹的名字
String parentFolder = file.getParent().substring(file.getParent().lastIndexOf("\\"));
//这个是存放解压之后的文件的路径
outputDirectory = unZipPath + parentFolder;
org.apache.tools.zip.ZipFile zipFile = new org.apache.tools.zip.ZipFile(ZipFilePath);
try {
Enumeration<?> e = zipFile.getEntries();
org.apache.tools.zip.ZipEntry zipEntry = null;
createDirectory(outputDirectory, "");
while (e.hasMoreElements()) {
zipEntry = (org.apache.tools.zip.ZipEntry) e.nextElement();
System.out.println("解压:" + zipEntry.getName());
if (zipEntry.isDirectory()) {
String name = zipEntry.getName();
name = name.substring(0, name.length() - 1);
File f = new File(outputDirectory + File.separator + name);
f.mkdir();
System.out.println("创建目录:" + outputDirectory + File.separator + name);
} else {
String fileName = zipEntry.getName();
fileName = fileName.replace('\\', '/');
if (fileName.indexOf("/") != -1) {
createDirectory(outputDirectory, fileName.substring(0,
fileName.lastIndexOf("/")));
fileName = fileName.substring(
fileName.lastIndexOf("/") + 1,
fileName.length());
}
File f = new File(outputDirectory + File.separator
+ zipEntry.getName());
f.createNewFile();
InputStream in = zipFile.getInputStream(zipEntry);
FileOutputStream out = new FileOutputStream(f);
byte[] by = new byte[1024];
int c;
while ((c = in.read(by)) != -1) {
out.write(by, 0, c);
}
in.close();
out.close();
}
}
System.out.println("解压 [" + zipFileName + "] 完成!");
} catch (Exception ex) {
System.out.println(ex.getMessage());
} finally {
zipFile.close();
}
}
return outputDirectory;
}
private static void createDirectory(String directory, String subDirectory) {
String[] dir;
File fl = new File(directory);
try {
if ("".equals(subDirectory) && fl.exists() != true) {
fl.mkdir();
} else if (!"".equals(subDirectory)) {
dir = subDirectory.replace('\\', '/').split("/");
for (int i = 0; i < dir.length; i++) {
File subFile = new File(directory + File.separator + dir[i]);
if (subFile.exists() == false) {
subFile.mkdir();
}
directory += File.separator + dir[i];
}
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
/**
* 连接SFTP 获取文件
*
* @param pagePath
* @param pageName
* @return
* @throws Exception
*/
public InputStream getFileFromSFTP(String pagePath, String pageName) throws Exception {
InputStream in = null;
login();
try {
byte[] bytes = download(pagePath, pageName);
in = new ByteArrayInputStream(bytes);
} catch (Exception e) {
logout();
throw new Exception("获取文件失败");
}
logout();
return in;
}
private void mkdir(String hostDir) throws SftpException {
sftp.mkdir(hostDir);
}
public Map<String, String> getFilesFromSFTP() {
login();
Map<String, String> map = new HashMap<>();
try {
sftp.cd(hostDir);
Vector<String> files = sftp.ls("*");
getFileList(files, map);
} catch (Exception e) {
logout();
e.printStackTrace();
throw new Exception("获取文件失败");
} finally {
logout();
return map;
}
}
public void getFileList(Vector<String> files, Map<String, String> map) throws Exception {
for (int i = 0; i < files.size(); i++) {
Object obj = files.elementAt(i);
if (obj instanceof com.jcraft.jsch.ChannelSftp.LsEntry) {
LsEntry entry = (LsEntry) obj;
if (!entry.getAttrs().isDir()) {
log.info("路径:" + sftp.pwd() + "---文件:" + entry.getFilename());
InputStream inputStream = downloadStream(entry.getFilename());
String result = new BufferedReader(new InputStreamReader(inputStream)).lines().collect(Collectors.joining(System.lineSeparator()));
map.put(sftp.pwd() + "/" + entry.getFilename(), result);
}
if (entry.getAttrs().isDir()) {
if (!".".equals(entry.getFilename()) && !"..".equals(entry.getFilename())) {
sftp.cd(entry.getFilename());
if (CollectionUtils.isEmpty(sftp.ls("*"))) {
log.info("路径:" + sftp.pwd() + "---文件夹:" + entry.getFilename());
sftp.cd("..");
} else {
log.info("路径:" + sftp.pwd() + "---文件夹:" + entry.getFilename());
getFileList(sftp.ls("*"), map);
}
}
}
}
if (i == files.size() - 1) {
sftp.cd("..");
}
}
}
public void isExist(String path, String name) throws Exception {
try {
sftp.cd(path);
SftpATTRS stat = sftp.stat(name);
} catch (Exception e) {
throw new Exception("文件不存在");
}
}
public static void main(String[] args) throws Exception {
String username = "";
String password = "";
String host = "";
int port = 22;
String hostDir = "/upload";
SFTPUtil sftpUtil = new SFTPUtil(username, password, host, port, hostDir);
sftpUtil.login();
// sftpUtil.isExist("/nfs/image/"+"a","ca30e214be8243eea7de9be8f015d807.jpg");
// String s = "F:/新华/222/3/4/";
// String[] dirs = s.split("/");
// File file = new File("F:/新华/222/3/4/44");
// if (!file.exists()) {
// file.mkdirs();
// }
String newPath = "/upload/11";
File file = new File("/upload/11");
if (!file.exists()) {
file.mkdirs();
}
String[] dirs = newPath.split("/");
String tempPath = "";
int index = 0;
// mkdirDir(dirs, tempPath, dirs.length, index);
// createDirectory("/upload/11","");
// sftpUtil.upload("/upload/11/22","F:\\新华\\11.jpg");
// sftpUtil.mkdirDir(dirs, tempPath, dirs.length, index);
// sftpUtil.download("/nfs/image/applet/img/2021/05/21/idimg", "4d1ad761b55847c4a023c4ffed422fc5.jpg", "F:/新华/222/3/4/44");
sftpUtil.logout();
}
/**
* 递归根据路径创建文件夹--运用ThreadLocal方式
*
* @param path 目的路径
* @return
*/
public void mkdirDirs(String path) {
Scope scope = threadLocal.get();
// tempPath 拼接路径 第一次调用给默认值 "" 即可
// index 数组下标 第一次调用给默认值 0 即可
if (ObjectUtils.isEmpty(scope)) {
scope = new Scope("", 0);
threadLocal.set(scope);
}
String tempPath = scope.getTempPath();
int index = scope.getIndex();
String[] dirs = path.split("/");
int length = dirs.length;
// 以"/a/b/c/d"为例按"/"分隔后,第0位是"";顾下标从1开始
index++;
if (index < length) {
// 目录不存在,则创建文件夹
tempPath += "/" + dirs[index];
}
try {
log.info("检测目录[" + tempPath + "]");
sftp.cd(tempPath);
if (index < length) {
threadLocal.set(new Scope(tempPath, index));
mkdirDirs(path);
}
} catch (SftpException ex) {
log.warn("创建目录[" + tempPath + "]");
try {
sftp.mkdir(tempPath);
sftp.cd(tempPath);
} catch (SftpException e) {
e.printStackTrace();
log.error("创建目录[" + tempPath + "]失败,异常信息[" + e.getMessage() + "]");
}
log.info("进入目录[" + tempPath + "]");
threadLocal.set(new Scope(tempPath, index));
mkdirDirs(path);
}
}
/**
* 递归根据路径创建文件夹
*
* @param dirs 根据 / 分隔后的数组文件夹名称
* @param tempPath 拼接路径
* @param length 文件夹的格式
* @param index 数组下标
* @return
*/
public void mkdirDir(String[] dirs, String tempPath, int length, int index) {
// 以"/a/b/c/d"为例按"/"分隔后,第0位是"";顾下标从1开始
index++;
if (index < length) {
// 目录不存在,则创建文件夹
tempPath += "/" + dirs[index];
}
try {
log.info("检测目录[" + tempPath + "]");
sftp.cd(tempPath);
if (index < length) {
mkdirDir(dirs, tempPath, length, index);
}
} catch (SftpException ex) {
log.warn("创建目录[" + tempPath + "]");
try {
sftp.mkdir(tempPath);
sftp.cd(tempPath);
} catch (SftpException e) {
e.printStackTrace();
log.error("创建目录[" + tempPath + "]失败,异常信息[" + e.getMessage() + "]");
}
log.info("进入目录[" + tempPath + "]");
mkdirDir(dirs, tempPath, length, index);
}
}
}
class Scope {
private String tempPath;
private int index;
public Scope(String tempPath, int index) {
this.tempPath = tempPath;
this.index = index;
}
public Scope() {
}
public String getTempPath() {
return tempPath;
}
public void setTempPath(String tempPath) {
this.tempPath = tempPath;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
@Override
public String toString() {
return "Scope{" +
"tempPath='" + tempPath + '\'' +
", index=" + index +
'}';
}
}
测试
package com.wh.sftp;
import org.apache.log4j.Logger;
public class Demo {
private static Logger log = Logger.getLogger(SFTPUtil.class);
public static void main(String[] args) throws Exception {
String username = "";
String password = "";
String host = "";
int port = 22;
String hostDir = "/upload";
SFTPUtil sftpUtil = new SFTPUtil(username, password, host, port, hostDir);
try {
sftpUtil.login();
} catch (Exception e) {
// sftpUtil.isExist("/nfs/image/"+"a","ca30e214be8243eea7de9be8f015d807.jpg");
// String s = "F:/新华/222/3/4/";
// String[] dirs = s.split("/");
// File file = new File("F:/新华/222/3/4/44");
// if (!file.exists()) {
// file.mkdirs();
// }
String newPath = "/upload/69/88";
//递归在sftp服务器上上传文件--ThreadLocal
//sftpUtil.mkdirDirs(newPath);
//sftpUtil.mkdirDir(dirs, tempPath, dirs.length, index);
sftpUtil.upload("/upload/668/88", "F:\\新华\\11.jpg");
//sftpUtil.download("/nfs/image/applet/img/2021/05/21/idimg", "4d1ad761b55847c4a023c4ffed422fc5.jpg", "F:/新华/222/3/4/44");
} finally {
sftpUtil.logout();
}
}
}