Java?項(xiàng)目連接并使用?SFTP?服務(wù)的示例詳解
SFTP(Secure File Transfer Protocol)是一種安全的文件傳輸協(xié)議,是SSH(Secure Shell)協(xié)議的一個子協(xié)議,設(shè)計(jì)用于加密和保護(hù)文件傳輸?shù)陌踩?。SSH本身是一種網(wǎng)絡(luò)協(xié)議,用于在不安全的網(wǎng)絡(luò)中提供安全的遠(yuǎn)程登錄和其他安全網(wǎng)絡(luò)服務(wù)。SFTP則在此基礎(chǔ)上,專注于文件的安全傳輸。
- 加密傳輸:SFTP使用加密來保護(hù)傳輸?shù)臄?shù)據(jù),包括文件內(nèi)容和認(rèn)證信息。
- 身份驗(yàn)證:SFTP使用SSH身份驗(yàn)證機(jī)制來驗(yàn)證用戶身份。用戶通常需要提供用戶名和密碼,或者使用SSH密鑰對進(jìn)行身份驗(yàn)證。
- 文件和目錄操作:SFTP支持文件和目錄的上傳、下載、刪除、重命名和創(chuàng)建等操作。
- 跨平臺兼容性:SFTP是一個跨平臺協(xié)議,可以在各種操作系統(tǒng)上使用,包括Linux、Unix、Windows等。
- 端到端數(shù)據(jù)完整性:SFTP確保傳輸?shù)奈募谠春湍繕?biāo)之間的完整性,防止數(shù)據(jù)在傳輸過程中被篡改或損壞。
- 可擴(kuò)展性:SFTP可以與其他協(xié)議和安全機(jī)制一起使用,以增強(qiáng)其功能。例如,它可以與公鑰基礎(chǔ)設(shè)施(PKI)一起使用以實(shí)現(xiàn)更高級的安全性。
SFTP通常用于許多場景,包括遠(yuǎn)程服務(wù)器維護(hù)、備份、文件共享和在不同計(jì)算機(jī)之間傳輸敏感數(shù)據(jù)。由于它提供了強(qiáng)大的安全性,因此特別適用于傳輸金融賬戶、公司文件和政府?dāng)?shù)據(jù)等敏感信息。
- 端口:SFTP的默認(rèn)端口與SSH相同,為22。這意味著只要sshd服務(wù)器啟動了,SFTP就可使用,不需要額外安裝。
- 守護(hù)進(jìn)程:SFTP本身沒有單獨(dú)的守護(hù)進(jìn)程,它必須使用SSHD守護(hù)進(jìn)程(端口號默認(rèn)是22)來完成相應(yīng)的連接操作。
- 配置:SFTP的配置通常與SSH配置相關(guān)。例如,可以通過修改SSH配置文件(如sshd_config)來啟用或禁用SFTP功能,以及設(shè)置相關(guān)的訪問權(quán)限和安全策略。
SFTP結(jié)合了SSH的安全性和文件傳輸?shù)谋憬菪?,成為許多組織和個人在傳輸敏感數(shù)據(jù)時的首選協(xié)議。
一、常見注意事項(xiàng)
1、如果客戶端不支持 ssh-rsa 協(xié)議時,需要在登陸的時候添加屬性:
config.put("HostKeyAlgorithms", "+ssh-rsa");…持續(xù)更新
二、添加第三方 pom 依賴
Java本身并不支持連接 SFTP,所以需要第三方依賴進(jìn)行連接。
<dependency> <groupId>com.jcraft</groupId> <artifactId>jsch</artifactId> <version>0.1.55</version> </dependency>
三、SFTPUtil 代碼
注意:如果客戶端不支持 ssh-rsa 協(xié)議時,需要添加屬性
package com.wen.util;
import com.jcraft.jsch.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.Date;
import java.util.Properties;
import java.util.Vector;
/**
* @author : rjw
* @date : 2024-10-14
*/
public class SFTPUtil {
private static final Logger logger = LoggerFactory.getLogger(SFTPUtil.class);
private Session session;
private ChannelSftp channelSftp;
/**
* 登錄
*/
public boolean login(String hostname, int port, String username, String password) {
try {
JSch jSch = new JSch();
// 根據(jù)用戶名,IP地址獲取一個session對象。
session = jSch.getSession(username, hostname, port);
session.setPassword(password);
// 避免第一次連接時需要輸入yes確認(rèn)
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
logger.info("成功連接服務(wù)器");
channelSftp = (ChannelSftp) session.openChannel("sftp");
channelSftp.connect();
logger.info("成功連接服務(wù)器");
return true;
} catch (JSchException e) {
logger.error("SFTPUtil login file error:", e);
}
return false;
}
/**
* 退出
*/
public void logout() {
if (channelSftp != null && channelSftp.isConnected()) {
try {
channelSftp.disconnect();
logger.info("成功退出 SFTP 服務(wù)器");
} catch (Exception e) {
logger.error("退出SFTP服務(wù)器異常: ", e);
} finally {
try {
channelSftp.disconnect(); // 關(guān)閉FTP服務(wù)器的連接
} catch (Exception e) {
logger.error("關(guān)閉SFTP服務(wù)器的連接異常: ", e);
}
}
}
if (session != null && session.isConnected()) {
try {
session.disconnect();
logger.info("成功退出 session 會話");
} catch (Exception e) {
logger.error("退出 session 會話異常: ", e);
} finally {
try {
session.disconnect(); // 關(guān)閉FTP服務(wù)器的連接
} catch (Exception e) {
logger.error("關(guān)閉 session 會話的連接異常: ", e);
}
}
}
}
/**
* 判斷是否連接
*/
public boolean isConnected() {
return channelSftp != null && channelSftp.isConnected();
}
/**
* 上傳文件
* 采用默認(rèn)的傳輸模式:OVERWRITE
*
* @param src 輸入流(文件)
* @param dst 上傳路徑
* @param fileName 上傳文件名
* @throws SftpException
*/
public boolean upLoadFile(InputStream src, String dst, String fileName) throws SftpException {
boolean isSuccess = false;
try {
if (createDir(dst)) {
channelSftp.put(src, fileName);
isSuccess = true;
}
} catch (SftpException e) {
logger.error(fileName + "文件上傳異常", e);
}
return isSuccess;
}
/**
* 創(chuàng)建一個文件目錄
*
* @param createPath 路徑
* @return
*/
public boolean createDir(String createPath) {
boolean isSuccess = false;
try {
if (isDirExist(createPath)) {
channelSftp.cd(createPath);
return true;
}
String[] pathArray = createPath.split("/");
StringBuilder filePath = new StringBuilder("/");
for (String path : pathArray) {
if (path.isEmpty()) {
continue;
}
filePath.append(path).append("/");
if (isDirExist(filePath.toString())) {
channelSftp.cd(filePath.toString());
} else {
// 建立目錄
channelSftp.mkdir(filePath.toString());
// 進(jìn)入并設(shè)置為當(dāng)前目錄
channelSftp.cd(filePath.toString());
}
}
channelSftp.cd(createPath);
isSuccess = true;
} catch (SftpException e) {
logger.error("目錄創(chuàng)建異常!", e);
}
return isSuccess;
}
/**
* 判斷目錄是否存在
*
* @param directory 路徑
* @return
*/
public boolean isDirExist(String directory) {
boolean isSuccess = false;
try {
SftpATTRS sftpATTRS = channelSftp.lstat(directory);
isSuccess = true;
return sftpATTRS.isDir();
} catch (Exception e) {
logger.info("SFTPUtil path has error:{}", directory);
logger.error("SFTPUtil path has error: ", e);
if (e.getMessage().equalsIgnoreCase("no such file")) {
isSuccess = false;
}
}
return isSuccess;
}
/**
* 重命名指定文件或目錄
*/
public boolean rename(String oldPath, String newPath) {
boolean isSuccess = false;
try {
channelSftp.rename(oldPath, newPath);
isSuccess = true;
} catch (SftpException e) {
logger.error("重命名指定文件或目錄異常", e);
}
return isSuccess;
}
/**
* 列出指定目錄下的所有文件和子目錄。
*/
public Vector ls(String path) {
try {
Vector vector = channelSftp.ls(path);
return vector;
} catch (SftpException e) {
logger.error("列出指定目錄下的所有文件和子目錄。", e);
}
return null;
}
/**
* 刪除文件
*/
public boolean deleteFile(String directory, String deleteFile) {
boolean isSuccess = false;
try {
channelSftp.cd(directory);
channelSftp.rm(deleteFile);
isSuccess = true;
} catch (SftpException e) {
logger.error("刪除文件失敗", e);
}
return isSuccess;
}
/**
* 下載文件
*
* @param directory 下載目錄
* @param downloadFile 下載的文件
* @param saveFile 下載到本地路徑
*/
public boolean download(String directory, String downloadFile, String saveFile) {
boolean isSuccess = false;
try {
channelSftp.cd(directory);
File file = new File(saveFile);
channelSftp.get(downloadFile, new FileOutputStream(file));
isSuccess = true;
} catch (SftpException e) {
logger.error("下載文件失敗", e);
} catch (FileNotFoundException e) {
logger.error("下載文件失敗", e);
}
return isSuccess;
}
/**
* 輸出指定文件流
*/
public InputStream getFile(String path) {
try {
InputStream inputStream = channelSftp.get(path);
return inputStream;
} catch (SftpException e) {
throw new RuntimeException(e);
}
}
/**
* 下載文件,新
*/
public InputStream downloadFile(String remoteFileName, String remoteFilePath) {
InputStream input = null;
try {
if (!isDirExist(remoteFilePath)) {
logger.info("SFTPUtil not changeWorkingDirectory.filename:{},Path:{}", remoteFileName, remoteFilePath);
logout();
return input;
}
logger.info("SFTPUtil Info filename:{},Path:{}", remoteFileName, remoteFilePath);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
channelSftp.get(remoteFileName, outputStream);
input = new ByteArrayInputStream(outputStream.toByteArray());
outputStream.close();
logger.info("file download. filename:{},Path:{}", remoteFileName, remoteFilePath);
} catch (Exception e) {
logger.error("SFTPUtil download file error:", e);
logout();
}
return input;
}
/**
* 驗(yàn)證sftp文件是否有效(判斷文件屬性的日期)
*/
public String validateSourceData(String fileName, String remoteFilePath, Date nowTime) throws IOException, SftpException {
Date temp = null;
if (!isDirExist(remoteFilePath)) {
logout();
return "嘗試切換SFTP路徑失敗, 文件路徑: " + remoteFilePath + fileName;
}
Vector vector = channelSftp.ls(remoteFilePath);
for (int i = 0; i < vector.capacity(); i++) {
ChannelSftp.LsEntry data = (ChannelSftp.LsEntry) vector.get(i);
if (data.getFilename().equalsIgnoreCase(fileName)) {
temp = new Date(data.getAttrs().getMTime() * 1000L);
//logger.info("時間: timeStamp:{},nowTime:{}",temp,nowTime);
if (temp.getTime() == nowTime.getTime()) {
return "SFTP文件沒有更新";
}
nowTime.setTime(temp.getTime());
break;
}
}
//logout();
return null;
}
/**
* IP
*/
public String getSFTPHost() {
return session.getHost();
}
/**
* 端口
*/
public int getSFTPPort() {
return session.getPort();
}
}四、測試示例
public class Test {
private static SFTPUtil sftpUtil = new SFTPUtil();
public static String convertInputStreamToString(InputStream inputStream) throws IOException{
StringBuilder stringBuilder = new StringBuilder();
try{
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
String line;
while ((line = bufferedReader.readLine()) != null){
System.out.println(line);
stringBuilder.append(line).append("\n");
}
}catch (Exception e){
System.out.println(e);
}
if(stringBuilder.length() > 0 && stringBuilder.charAt(stringBuilder.length() - 1) == '\n'){
stringBuilder.setLength(stringBuilder.length() - 1);
}
return stringBuilder.toString();
}
public static void main(String[] args) {
boolean connected = sftpUtil.isConnected();
if (!connected) {
System.out.println("連接SFTP");
// 折里可以采用配置文件 @Value 注解
connected = sftpUtil.login("10.26.73.163", 2222, "username", "password");
}
if (connected) {
System.out.println("連接成功");
System.out.println(sftpUtil.getSFTPHost() + " : " + sftpUtil.getSFTPPort());
InputStream inputStream = sftpUtil.getFile("/rjw/wind.txt");
String s = convertInputStreamToString(inputStream);
System.out.println(s);
File file = new File("C:\\Users\\wen\\Desktop\\sun.txt");
InputStream inputStream1 = Files.newInputStream(file.toPath());
boolean dir = sftpUtil.upLoadFile(inputStream1, "/rjw", "big.txt");
System.out.println("添加文件成功: " + dir);
sftpUtil.logout();
}
}
}五、測試結(jié)果

到此這篇關(guān)于Java 項(xiàng)目如何連接并使用 SFTP 服務(wù)的示例詳解的文章就介紹到這了,更多相關(guān)Java使用 SFTP 服務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java將Exception信息轉(zhuǎn)為String字符串的方法
今天小編就為大家分享一篇Java將Exception信息轉(zhuǎn)為String字符串的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-10-10
Java中將異步調(diào)用轉(zhuǎn)為同步的五種實(shí)現(xiàn)方法
本文介紹了將異步調(diào)用轉(zhuǎn)為同步阻塞模式的五種方法:wait/notify、ReentrantLock+Condition、Future、CountDownLatch和CyclicBarrier,每種方法都有其適用場景和核心機(jī)制,可以根據(jù)具體需求選擇合適的方法,需要的朋友可以參考下2025-02-02
JAVA正則表達(dá)式及字符串的替換與分解相關(guān)知識總結(jié)
今天給大家?guī)淼氖顷P(guān)于Java的相關(guān)知識總結(jié),文章圍繞著JAVA正則表達(dá)式及字符串的替換與分解展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06
Springboot整合Netty實(shí)現(xiàn)RPC服務(wù)器的示例代碼
這篇文章主要介紹了Springboot整合Netty實(shí)現(xiàn)RPC服務(wù)器的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01

