- 最近在工作功能使用了sftp做文件上傳下載的功能,在這裏簡單的記錄一下,
- pom文件中引入相關的jar包
<!-- https://mvnrepository.com/artifact/com.jcraft/jsch -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>
- 建立springboot項目,在application.properties添加如下配置
sftp.ip=127.0.0.1
sftp.port=22
sftp.username=xuyy
sftp.password=paswpord
#ftp根目錄
sftp.rootpath="D:SFTP/
- 上面一sftp開頭的都是自定義配置,需要寫個配置類讀取一下,自動注入到springboot中
package com.uinnova.ftpsynweb.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 特點: 讀取配置文件。可以對靜態變量直接賦值
*
* @author xuyangyang
*/
@Component
@ConfigurationProperties(prefix = "sftp")
@Data
public class SftpConfig {
public static String ip;
public static Integer port;
public static String username;
public static String password;
public static String rootpath;
//注意這裏是 static 修飾,便於sftputil直接取值
public static String getIp() {
return ip;
}
public void setIp(String ip) {
SftpConfig.ip = ip;
}
public static Integer getPort() {
return port;
}
public void setPort(Integer port) {
SftpConfig.port = port;
}
public static String getUsername() {
return username;
}
public void setUsername(String username) {
SftpConfig.username = username;
}
public static String getPassword() {
return password;
}
public void setPassword(String password) {
SftpConfig.password = password;
}
public static String getRootpath() {
return rootpath;
}
public void setRootpath(String rootpath) {
SftpConfig.rootpath = rootpath;
}
}
- 下面是具體的工具類,代碼寫的比較簡單,可以自己下載優化一下,等我有時間在優化,
package com.uinnova.ftpsynweb.util;
import com.jcraft.jsch.*;
import com.uinnova.ftpsynweb.config.SftpConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thymeleaf.util.StringUtils;
import javax.transaction.SystemException;
import java.io.*;
import java.util.*;
/**
* SFTP工具類
*/
@Slf4j
@Component
public class SftpUtil {
@Autowired
SftpConfig sftpConfig;
private static String sftp_ip = SftpConfig.getIp();
private static Integer sftp_port = SftpConfig.getPort();
private static String sftp_username = SftpConfig.getUsername();
private static String sftp_password = SftpConfig.getPassword();
/**
* sftp存儲根目錄
*/
public static String windows_path = "D:SFTP/";
/**
* sftp存儲根目錄
*/
public static String linux_path = "/home/xuyy/";
/**
* session
*/
private Session session;
/**
* 通道
*/
private ChannelSftp channel;
/**
* 規避多線程併發不斷開問題
*/
private volatile static ThreadLocal<SftpUtil> sftpLocal = new ThreadLocal<>();
/**
* 私有構造方法
*/
private SftpUtil() {
}
/**
* 構造函數
* 非線程安全,故權限爲私有
*/
private SftpUtil(String host, Integer port, String username, String password) {
super();
init(host, port, username, password);
}
/**
* 獲取本地線程存儲的sftp客戶端
*
* @return
* @throws Exception
*/
public static SftpUtil getSftpUtil() {
SftpUtil sftpUtil = sftpLocal.get();
if (null == sftpUtil || !sftpUtil.isConnected()) {
sftpLocal.set(new SftpUtil(sftp_ip, sftp_port, sftp_username, sftp_password));
}
return sftpLocal.get();
}
/**
* 獲取本地線程存儲的sftp客戶端
*
* @param host
* @param port
* @param username
* @param password
* @return
*/
public static SftpUtil getSftpUtil(String host, Integer port, String username, String password) {
//todo 該方法需要修改,如果線程本地存儲有連接則不會使用新的地址,可以測試使用
SftpUtil sftpUtil = sftpLocal.get();
if (null == sftpUtil || !sftpUtil.isConnected()) {
log.info("建立連接");
sftpLocal.set(new SftpUtil(host, port, username, password));
} else {
log.info("連接已經存在");
}
return sftpLocal.get();
}
/**
* 初始化 創建一個新的 SFTP 通道
*
* @param host
* @param port
* @param username
* @param password
*/
private void init(String host, Integer port, String username, String password) {
try {
//場景JSch對象
JSch jSch = new JSch();
// jsch.addIdentity(); 私鑰
session = jSch.getSession(username, host, port);
// 第一次登陸時候提示, (ask|yes|no)
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
config.put("compression.s2c", "zlib,none");
config.put("compression.c2s", "zlib,none");
session.setConfig(config);
//設置超時
//session.setTimeout(3*1000);
//設置密碼
session.setPassword(password);
session.connect();
//打開SFTP通道
channel = (ChannelSftp) session.openChannel("sftp");
//建立SFTP通道的連接
channel.connect();
} catch (JSchException e) {
log.error(e.getMessage());
}
}
/**
* 是否已連接
*
* @return
*/
private boolean isConnected() {
return null != channel && channel.isConnected();
}
/**
* 關閉通道
*/
public void closeChannel() {
if (null != channel) {
try {
channel.disconnect();
} catch (Exception e) {
log.error("關閉SFTP通道發生異常:", e);
}
}
if (null != session) {
try {
session.disconnect();
} catch (Exception e) {
log.error("SFTP關閉 session異常:", e);
}
}
}
/**
* 釋放本地線程存儲的sftp客戶端
*/
public static void release() {
if (null != sftpLocal.get()) {
sftpLocal.get().closeChannel();
sftpLocal.set(null);
}
}
/**
* 列出目錄下文件,只列出文件名字,沒有類型
*
* @param dir 目錄
* @return
*/
public List list(String dir) {
if (channel == null) {
log.error("獲取sftp連接失敗,請檢查" + sftp_ip + +sftp_port + "@" + sftp_username + "" + sftp_password + "是否可以訪問");
return null;
}
Vector<ChannelSftp.LsEntry> files = null;
try {
files = channel.ls(dir);
} catch (SftpException e) {
log.error(e.getMessage());
}
if (null != files) {
List fileNames = new ArrayList<String>();
Iterator<ChannelSftp.LsEntry> iter = files.iterator();
while (iter.hasNext()) {
String fileName = iter.next().getFilename();
if (StringUtils.equals(".", fileName) || StringUtils.equals("..", fileName)) {
continue;
}
fileNames.add(fileName);
}
return fileNames;
}
return null;
}
/**
* 列出文件詳情
*
* @param dir
* @return
*/
public List listDetail(String dir) {
if (channel == null) {
log.error("獲取sftp連接失敗,請檢查" + sftp_ip + +sftp_port + "@" + sftp_username + "" + sftp_password + "是否可以訪問");
return null;
}
Vector<ChannelSftp.LsEntry> files = null;
try {
files = channel.ls(dir);
} catch (SftpException e) {
log.error(e.getMessage());
}
if (null != files) {
List<Map<String, String>> fileList = new ArrayList<>();
Iterator<ChannelSftp.LsEntry> iter = files.iterator();
while (iter.hasNext()) {
ChannelSftp.LsEntry next = iter.next();
Map<String, String> map = new HashMap<>();
String fileName = next.getFilename();
if (StringUtils.equals(".", fileName) || StringUtils.equals("..", fileName)) {
continue;
}
String size = String.valueOf(next.getAttrs().getSize());
long mtime = next.getAttrs().getMTime();
String type = "";
String longname = String.valueOf(next.getLongname());
if (longname.startsWith("-")) {
type = "file";
} else if (longname.startsWith("d")) {
type = "dir";
}
map.put("name", fileName);
map.put("size", size);
map.put("type", type);
map.put("mtime", DateTimeUtil.timestampToDate(mtime));
fileList.add(map);
}
return fileList;
}
return null;
}
public List listDetail(String rootDir, String remotePath) {
if (channel == null) {
log.error("獲取sftp連接失敗,請檢查" + sftp_ip + +sftp_port + "@" + sftp_username + "" + sftp_password + "是否可以訪問");
return null;
}
Vector<ChannelSftp.LsEntry> files = null;
try {
channel.cd(rootDir);
files = channel.ls(remotePath);
} catch (SftpException e) {
log.error(e.getMessage());
}
if (null != files) {
List<Map<String, String>> fileList = new ArrayList<>();
Iterator<ChannelSftp.LsEntry> iter = files.iterator();
while (iter.hasNext()) {
ChannelSftp.LsEntry next = iter.next();
Map<String, String> map = new HashMap<>();
String fileName = next.getFilename();
if (StringUtils.equals(".", fileName) || StringUtils.equals("..", fileName)) {
continue;
}
String size = String.valueOf(next.getAttrs().getSize());
long mtime = next.getAttrs().getMTime();
String type = "";
String longname = String.valueOf(next.getLongname());
if (longname.startsWith("-")) {
type = "file";
} else if (longname.startsWith("d")) {
type = "dir";
}
map.put("name", fileName);
map.put("size", size);
map.put("type", type);
map.put("mtime", DateTimeUtil.timestampToDate(mtime));
fileList.add(map);
}
return fileList;
}
return null;
}
/**
* @param file 上傳文件
* @param remotePath 服務器存放路徑,支持多級目錄
* @throws SystemException
*/
public void upload(File file, String remotePath) throws SystemException {
if (channel == null) {
log.error("獲取sftp連接失敗,請檢查" + sftp_ip + +sftp_port + "@" + sftp_username + "" + sftp_password + "是否可以訪問");
}
FileInputStream fileInputStream = null;
try {
if (file.isFile()) {
String rpath = remotePath;//服務器要創建的目錄
try {
createDir(rpath);
} catch (Exception e) {
throw new SystemException("創建路徑失敗:" + rpath);
}
channel.cd(remotePath);
System.out.println(remotePath);
fileInputStream = new FileInputStream(file);
channel.put(fileInputStream, file.getName());
}
} catch (FileNotFoundException e) {
throw new SystemException("上傳文件沒有找到");
} catch (SftpException e) {
throw new SystemException("上傳ftp服務器錯誤");
} finally {
try {
if (fileInputStream != null) {
fileInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* @param file 上傳文件
* @param remoteName 上傳文件名字
* @param remotePath 服務器存放路徑,支持多級目錄
* @throws SystemException
*/
public boolean upload(File file, String remoteName, String remotePath) {
if (channel == null) {
System.out.println("get sftp connect fail,please reboot sftp client");
log.error("獲取sftp連接失敗,請檢查" + sftp_ip + +sftp_port + "@" + sftp_username + "" + sftp_password + "是否可以訪問");
} else {
FileInputStream fileInputStream = null;
try {
if (file.isFile()) {
//服務器要創建的目錄
String rpath = remotePath;
createDir(rpath);
channel.cd(remotePath);
log.error(remotePath + " " + remoteName);
fileInputStream = new FileInputStream(file);
channel.put(fileInputStream, remoteName);
return true;
}
} catch (FileNotFoundException e) {
log.error(e.getMessage(), "上傳文件沒有找到");
return false;
} catch (SftpException e) {
log.error(e.getMessage());
return false;
} finally {
try {
if (fileInputStream != null) {
fileInputStream.close();//這裏要關閉文件流
} else {
System.out.println("流不存在" + remotePath + " " + remoteName);
log.error("流不存在" + remotePath + " " + remoteName);
}
} catch (IOException e) {
e.printStackTrace();
}
// try to delete the file immediately
// boolean deleted = false;
// try {
// deleted = file.delete();
// } catch (SecurityException e) {
// log.error(e.getMessage());
// }
// // else delete the file when the program ends
// if (deleted) {
// System.out.println("Temp file deleted.");
// log.info("Temp file deleted.");
// } else {
// file.deleteOnExit();
// System.out.println("Temp file scheduled for deletion.");
// log.info("Temp file scheduled for deletion.");
// }
}
}
return false;
}
public boolean upload(InputStream inputStream, String remoteName, String remotePath) {
if (channel == null) {
System.out.println("get sftp connect fail,please reboot sftp client");
log.error("獲取sftp連接失敗,請檢查" + sftp_ip + +sftp_port + "@" + sftp_username + "" + sftp_password + "是否可以訪問");
} else {
try {
//服務器要創建的目錄
String rpath = remotePath;
createDir(rpath);
channel.cd(remotePath);
log.debug(remotePath + " " + remoteName);
channel.put(inputStream, remoteName);
return true;
} catch (SftpException e) {
log.error(e.getMessage());
return false;
} finally {
try {
if (inputStream != null) {
inputStream.close();//這裏要關閉文件流
} else {
System.out.println("流不存在" + remotePath + " " + remoteName);
log.error("流不存在" + remotePath + " " + remoteName);
}
} catch (IOException e) {
e.printStackTrace();
}
// try to delete the file immediately
// boolean deleted = false;
// try {
// deleted = file.delete();
// } catch (SecurityException e) {
// log.error(e.getMessage());
// }
// // else delete the file when the program ends
// if (deleted) {
// System.out.println("Temp file deleted.");
// log.info("Temp file deleted.");
// } else {
// file.deleteOnExit();
// System.out.println("Temp file scheduled for deletion.");
// log.info("Temp file scheduled for deletion.");
// }
}
}
return false;
}
/**
* 下載文件
*
* @param downDir 下載目錄
* @param src 源文件
* @param dst 保存後的文件名稱或目錄
* @throws Exception
*/
public void downFile(String downDir, String src, String dst) {
try {
channel.cd(downDir);
channel.get(src, dst);
} catch (SftpException e) {
log.error(e.getMessage());
}
}
public void downFiles(String rootDir, String src, String dst) throws Exception {
channel.cd(rootDir);
channel.get(src, dst);
}
/**
* 下載文件
*
* @param rootDir
* @param targetPath
* @return
*/
public File downFile(String rootDir, String targetPath) {
OutputStream outputStream = null;
File file = null;
try {
channel.cd(rootDir);
file = new File(targetPath.substring(targetPath.lastIndexOf("/") + 1));
outputStream = new FileOutputStream(file);
channel.get(targetPath, outputStream);
} catch (SftpException e) {
log.error(e.getMessage());
} catch (FileNotFoundException e) {
log.error(e.getMessage());
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return file;
}
public Vector listFile(String rootDir, String remotePath) throws SftpException {
channel.cd(rootDir);
return channel.ls(remotePath);
}
/**
* 列出目錄下的文件
*
* @param directory:要列出的目錄
*/
public Vector listFile(String directory) throws SftpException {
return channel.ls(directory);
}
/**
* 刪除文件
*
* @param filePath 文件全路徑
* @throws SftpException
*/
public void deleteFile(String filePath) throws SftpException {
channel.rm(filePath);
}
/**
* 創建一個文件目錄
*/
public void createDir(String createpath) {
try {
if (isDirExist(createpath)) {
this.channel.cd(createpath);
return;
}
String pathArry[] = createpath.split("/");
StringBuffer filePath = new StringBuffer("/");
for (String path : pathArry) {
if (path.equals("")) {
continue;
}
filePath.append(path + "/");
if (isDirExist(filePath.toString())) {
channel.cd(filePath.toString());
} else {
// 建立目錄
channel.mkdir(filePath.toString());
// 進入並設置爲當前目錄
channel.cd(filePath.toString());
}
}
this.channel.cd(createpath);
} catch (SftpException e) {
log.error(e.getMessage());
}
}
/**
* 判斷目錄是否存在
*/
public boolean isDirExist(String directory) {
boolean isDirExistFlag = false;
try {
SftpATTRS sftpATTRS = channel.lstat(directory);
isDirExistFlag = true;
return sftpATTRS.isDir();
} catch (Exception e) {
if (e.getMessage().toLowerCase().equals("no such file")) {
isDirExistFlag = false;
}
}
return isDirExistFlag;
}
static String PS = "/";
/**
* This method is called recursively to download the folder content from SFTP server
*
* @param sourcePath
* @param destinationPath
* @throws SftpException
*/
public void recursiveFolderDownload(String sourcePath, String destinationPath) throws SftpException {
Vector<ChannelSftp.LsEntry> fileAndFolderList = channel.ls(sourcePath); // Let list of folder content
//Iterate through list of folder content
for (ChannelSftp.LsEntry item : fileAndFolderList) {
if (!item.getAttrs().isDir()) { // Check if it is a file (not a directory).
if (!(new File(destinationPath + PS + item.getFilename())).exists()
|| (item.getAttrs().getMTime() > Long
.valueOf(new File(destinationPath + PS + item.getFilename()).lastModified()
/ (long) 1000)
.intValue())) { // Download only if changed later.
new File(destinationPath + PS + item.getFilename());
channel.get(sourcePath + PS + item.getFilename(),
destinationPath + PS + item.getFilename()); // Download file from source (source filename, destination filename).
}
} else if (!(".".equals(item.getFilename()) || "..".equals(item.getFilename()))) {
new File(destinationPath + PS + item.getFilename()).mkdirs(); // Empty folder copy.
recursiveFolderDownload(sourcePath + PS + item.getFilename(),
destinationPath + PS + item.getFilename()); // Enter found folder on server to read its contents and create locally.
}
}
}
/**
* 文件夾不存在,創建
*
* @param folder 待創建的文件節夾
*/
public void createFolder(String folder) {
SftpATTRS stat = null;
try {
stat = channel.stat(folder);
} catch (SftpException e) {
log.debug("複製目的地文件夾" + folder + "不存在,創建");
}
if (stat == null) {
try {
channel.mkdir(folder);
} catch (SftpException e) {
log.error("創建失敗", e.getCause());
}
}
}
public InputStream get(String filePath) {
InputStream inputStream = null;
try {
inputStream = channel.get(filePath);
} catch (SftpException e) {
log.error(e.getMessage());
}
return inputStream;
}
public void put(InputStream inputStream, String filePath) {
try {
channel.put(inputStream, filePath);
} catch (SftpException e) {
log.error(e.getMessage());
}
}
public Vector<ChannelSftp.LsEntry> ls(String filePath) {
Vector ls = null;
try {
ls = channel.ls(filePath);
} catch (SftpException e) {
log.error(e.getMessage());
}
return ls;
}
/**
* 複製文件夾
*
* @param src 源文件夾
* @param desc 目的文件夾
*/
public void copy(String src, String desc) {
// 檢查目的文件存在與否,不存在則創建
this.createDir(desc);
// 查看源文件列表
Vector<ChannelSftp.LsEntry> fileAndFolderList = this.ls(src);
for (ChannelSftp.LsEntry item : fileAndFolderList) {
if (!item.getAttrs().isDir()) {//是一個文件
try (InputStream tInputStream = this.get(src + PS + item.getFilename());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream nInputStream = new ByteArrayInputStream(baos.toByteArray())
) {
byte[] buffer = new byte[1024];
int len;
while ((len = tInputStream.read(buffer)) > -1) {
baos.write(buffer, 0, len);
}
baos.flush();
this.put(nInputStream, desc + PS + item.getFilename());
} catch (IOException e) {
log.error(e.getMessage());
}
// 排除. 和 ..
} else if (!(".".equals(item.getFilename()) || "..".equals(item.getFilename()))) {
// 創建文件,可能不需要
this.createFolder(desc + PS + item.getFilename());
//遞歸複製文件
copy(src + PS + item.getFilename(), desc + PS + item.getFilename());
}
}
}
public static void main(String[] args) throws Exception {
// SftpUtil sftpUtil = SftpUtil.getSftpUtil(" ", 22, " ", "");
SftpUtil sftpUtil = SftpUtil.getSftpUtil(" ", 22, " ", " ");
// List vector = sftpUtil.listDetail("/home/xuyy/", "clients/CustomModel/13687/65mdoxkmic5lawrtshku8j5km3lre0zp");
String SFTPWORKINGDIR = "/home/xuyy"; // Source Directory on SFTP server
// String LOCALDIRECTORY = "e:\\temp"; // Local Target Directory
// sftpUtil.recursiveFolderDownload(SFTPWORKINGDIR, LOCALDIRECTORY);
// System.out.println(vector.toString());
// String path = "clients/ThingJS/23997/239971558411726943/";
// /home/xuyy/clients/CustomModel/13687/65mdoxkmic5lawrtshku8j5km3lre0zp
String path = "/home/xuyy/";
// String path = "/home/xuyy/clients/ThingJs/13640/20190425153300016828239";
// File file1 = sftpUtil.downFile(linux_path, path + "index.json");
// File file2 = sftpUtil.downFile(linux_path, path + "preview.jpg");
// File file3 = sftpUtil.downFile(linux_path, path + "scene.json");
// sftpUtil.downFiles(path, "clients/ThingJS/13640/111/LoginLog.class", "D:\\SFTP\\clients\\ThingJS\\13824");
// sftpUtil.downFile(path, "LoginLog.class", "D:\\SFTP\\clients\\ThingJS\\13824");
sftpUtil.upload(new File("f://test1.txt"), "test.txt", path + "/1");
if (sftpUtil.isConnected()) {
SftpUtil.release();
}
}
}
- 下面是具體的幾個接口,這裏也貼出來了,方便大家使用
package com.uinnova.ftpsynweb.controller;
import com.uinnova.ftpsynweb.util.Return;
import com.uinnova.ftpsynweb.util.SftpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.thymeleaf.util.StringUtils;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;
/**
* @author xuyangyang
*/
@Slf4j
@RestController
public class FileController {
/**
* @param file 上傳文件
* @param targetPath 保存文件路徑
* @param fileName 上傳文件名字
* @return
* @throws IOException
*/
@RequestMapping(value = "/file/upload")
@ResponseBody
public Return upload(@RequestParam("file") MultipartFile file, String targetPath, String fileName) throws IOException {
InputStream uploadFile = file.getInputStream();
SftpUtil sftpUtil = SftpUtil.getSftpUtil();
// win上傳
boolean upload = sftpUtil.upload(uploadFile, fileName, targetPath);
//linux 上傳
//boolean upload = sftpUtil.upload(uploadFile, fileName, SftpConfig.getRootpath()+targetPath);
return Return.Ok(upload);
}
/**
* @param targetPath 需要下載的文件具體路徑
* @param response
* @throws IOException
*/
@RequestMapping(value = "/file/download")
@ResponseBody
public void download(String targetPath, HttpServletResponse response) throws IOException {
SftpUtil sftpUtil = SftpUtil.getSftpUtil();
// File upload = sftpUtil.downFile(SftpConfig.getRootpath(), targetPath);
if (StringUtils.isEmpty(targetPath) || !targetPath.contains("/")) {
log.error("下載路徑不正確" + targetPath);
return;
}
System.out.println(targetPath);
String fileName = targetPath.substring(targetPath.lastIndexOf("/") + 1);
log.info(fileName);
File upload = sftpUtil.downFile("/", targetPath);
InputStream inputStream = new FileInputStream(upload);
OutputStream outputStream = response.getOutputStream();
response.setHeader("content-type", "application/octet-stream");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
IOUtils.copy(inputStream, outputStream);
outputStream.flush();
}
/**
* 獲取sftp下文件消息列表
*
* @param filePath 文件路徑
* @return
*/
@RequestMapping(value = "/file/list")
@ResponseBody
public Return list(@RequestParam("filePath") String filePath) {
log.info("獲取路徑下列表 :{}", filePath);
SftpUtil sftpUtil = SftpUtil.getSftpUtil();
List list = sftpUtil.listDetail(filePath);
return Return.Ok(list);
}
/**
* sftp內複製文件夾
*
* @param src 源文件夾
* @param desc 目的文件夾
* @return
*/
@RequestMapping(value = "file/copy")
@ResponseBody
public Return copy(String src, String desc) {
SftpUtil sftpUtil = SftpUtil.getSftpUtil();
sftpUtil.copy(src, desc);
return Return.Ok("複製成功");
}
public static void main(String[] args) {
}
}