SFTP服務配置以及命令/代碼操作


摘自:https://blog.csdn.net/zhichao_qzc/article/details/80301994

一、SFTP簡述

二、SFTP服務配置(基於CentOS 7)

三、SFTP常用命令

四、Java代碼實現SFTP操作(JSch實現上傳、下載、監視器)

五、踩坑記錄

一、SFTP簡述
sftp(Secure File Transfer Protocol)是一種安全的文件傳送協議,是ssh內含協議,也就是說只要sshd服務器啟動了,sftp就可使用,不需要額外安裝,它的默認端口和SSH一樣為22。
sftp通過使用加密/解密技術來保障傳輸文件的安全性,因此sftp的傳輸效率比普通的FTP要低,但sftp的安全性要比ftp高,因此sftp通常用於報表、對賬單等對安全性要求較高的場景。

二、SFTP服務配置(基於Centos 7)
在CentOS 7系統中按照如下步驟配置sftp服務
1、使用root用戶查看openssh的版本:版本需大於4.8p1

ssh -V // 如果版本過低,可以使用 yun update 進行更新

2、使用root用戶創建用戶組,組名為sftpgroup;創建sftp用戶,用戶名為sftpuser,並設置密碼

groupadd sftpgroup // 創建sftp組
useradd -g sftpgroup -M -s /sbin/nologin sftpuser //-M 表示創建用戶時不生成對應home目錄,-s /sbin/nologin 表示sftp用戶不能登錄系統
passwd sftpuser // 修改sftp用戶密碼

3、修改配置文件sshd_config

vi /etc/ssh/sshd_config

修改如下:

將下面這行注釋掉
#Subsystem sftp /usr/libexec/openssh/sftp-server
## 在文件末尾添加如下幾行
Subsystem sftp internal-sftp
Match Group sftpgroup
X11Forwarding no
AllowTcpForwarding no
ChrootDirectory %h
ForceCommand internal-sftp

4、使用root用戶新建目錄/home/sftpfile,將其設置為sftpuser的home目錄,並指定目錄權限

mkdir -p /sftp/sftpuser //-p 表示parents,即遞歸創建目錄
usermod -d /sftp/sftpuser sftpuser // -d 表示修改用戶home目錄

// 設置Chroot目錄權限
chown root:sftpgroup /sftp/sftpuser
chmod 755 /sftp/sftpuser

// 設置sftp用戶可以操作的目錄
mkdir /sftp/sftpuser/upload
chown sftpuser:sftpgroup /sftp/sftpuser/upload
chmod 755 /sftp/sftpuser/upload

5、重啟SSH

systemctl restart sshd.service

6、驗證:切換到sftpuser用戶進行驗證

sftp sftpuser@127.0.0.1

三、SFTP常用命令
sftp的常用命令和ftp基本相同,使用help命令即可查詢

 

四、Java代碼實現SFTP操作
Java操作sftp需要使用一個開源包jsch,官網地址為 http://www.jcraft.com/jsch/,Maven項目中通過在pom.xml中引入如下依賴,如果需要其他版本,可在Maven中央倉庫http://mvnrepository.com/ 查詢。

<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>


Jsch提供了sftp的各類操作的Java實現,ChannelSftp類是實現SFTP操作的核心類,sftp的命令即為該類中的方法,可以對比上圖的sftp常用命令,如:sftp命令中ls為展示目錄下的文件列表,則ChannelSftp類中有 ls() 方法與其對應。

####1、sftp服務連接和關閉

private void connect(SFTPConfig sftpConfig) {
try {
// 通過JSch對象獲取session對象
session = new JSch().getSession(
sftpConfig.getSftpUserName(), // sftp用戶名
sftpConfig.getSftpHost(), // sftp主機IP
sftpConfig.getSftpPort()); // sftp端口
if (null != sftpConfig.getSftpPassword()) {
session.setPassword(sftpConfig.getSftpPassword()); // sftp用戶密碼
}
if (null != sftpConfig.getTimeout()) {
session.setTimeout(sftpConfig.getTimeout()); // 超時時間
}
session.setConfig("StrictHostKeyChecking", "no"); // 讓ssh客戶端自動接受新主機的hostkey
session.connect();

this.channelSftp = (ChannelSftp) session.openChannel("sftp"); // 打開sftp渠道,除sftp外還有shell、X11等類型
this.channelSftp.connect();

} catch (JSchException e) {
this.close();
e.printStackTrace();
}
}

public void close() {
channelSftp.quit();
if (null != channelSftp) {
channelSftp.disconnect();
}
if (null != session) {
session.disconnect();
}
}

 

####2、JSch的傳輸模式
JSch有三種文件傳輸模式:
(1)OVERWRITE:完全覆蓋模式。JSch的默認文件傳輸模式,傳輸的文件將覆蓋目標文件。
(2)APPEND:追加模式。如果目標文件已存在,則在目標文件后追加。
(3)RESUME:恢復模式。如果文件正在傳輸時,由於網絡等原因導致傳輸中斷,則下一次傳輸相同的文件
時,會從上一次中斷的地方續傳。

####3、sftp上傳
JSch為每種傳輸模式提供了3類不同的上傳方法
(1)最常用也是最簡單的調用

/**
* @param sftpParams
* @param channelSftpModel 調用的模式: ChannelSftp.OVERWRITE,ChannelSftp.APPEND,ChannelSftp.RESUME
* @throws SFTPException
*/
public void upload(SFTPParams sftpParams, int channelSftpModel) throws SFTPException {
try {
channelSftp.put(sftpParams.getLocalFilepath(), sftpParams.getRemoteFilepath(), channelSftpModel);
} catch (SftpException e) {
throw new SFTPException("Upload [" + sftpParams.getLocalFilepath() + "] to SFTP "
+ sftpConfig.getSftpHost() + ":" + sftpConfig.getSftpPort()
+ "[" + sftpParams.getRemoteFilepath() + "]" + " error.", e);
}
}

(2)基於InputStream的調用
這種方法適用於原始文件不存在,需要保存到遠程目錄的數據來源於網絡或者代碼生成,當然原始文件如果存在,也可以通過FileInputStream上傳。

/**
*
* @param sftpParams
* @param channelSftpModel 調用的模式: ChannelSftp.OVERWRITE,ChannelSftp.APPEND,ChannelSftp.RESUME
* @param src 輸入流
* @throws SFTPException
*/
public void upload(SFTPParams sftpParams, int channelSftpModel, InputStream src) throws SFTPException {
try {
channelSftp.put(src, sftpParams.getRemoteFilepath(), channelSftpModel);
} catch (SftpException e) {
throw new SFTPException("Upload [" + sftpParams.getLocalFilepath() + "] to SFTP "
+ sftpConfig.getSftpHost() + ":" + sftpConfig.getSftpPort()
+ "[" + sftpParams.getRemoteFilepath() + "]" + " error.", e);
}
}

 


(3)基於OutputStream的調用
通過向put()方法返回的輸出流中寫入數據的方式來保存文件,這種方式可以自定義輸出流的數據塊大小(Jsch默認數據塊大小為32KB)

/**
* @param sftpParams
* @param channelSftpModel 調用的模式: ChannelSftp.OVERWRITE,ChannelSftp.APPEND,ChannelSftp.RESUME
* @param src 輸入流
* @param bufferSize 數據塊大小
* @throws SFTPException
*/
public void upload(SFTPParams sftpParams, int channelSftpModel, InputStream src, int bufferSize) throws SFTPException {
OutputStream out = null;
try {
out = channelSftp.put(sftpParams.getRemoteFilepath(), channelSftpModel);
byte[] buff = new byte[bufferSize]; // 設定每次傳輸的數據塊大小
int read;
if (out != null) {
do {
read = src.read(buff, 0, buff.length);
if (read > 0) {
out.write(buff, 0, read);
}
out.flush();
} while (read >= 0);
}
} catch (IOException e) {
throw new SFTPException("Upload [" + sftpParams.getLocalFilepath() + "] to SFTP "
+ sftpConfig.getSftpHost() + ":" + sftpConfig.getSftpPort()
+ "[" + sftpParams.getRemoteFilepath() + "]" + " error.", e);
} catch (SftpException e) {
throw new SFTPException("Upload [" + sftpParams.getLocalFilepath() + "] to SFTP "
+ sftpConfig.getSftpHost() + ":" + sftpConfig.getSftpPort()
+ "[" + sftpParams.getRemoteFilepath() + "]" + " error.", e);
} finally {
if (null != out) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

####4、sftp下載
JSch提供了3類不同的下載方法,JSch提供的下載方法,如果沒有顯示指明傳輸模式,則默認為覆蓋模式
(1)最常用也是最簡單的調用

public void download(SFTPParams sftpParams) throws SFTPException {
try {
channelSftp.get(sftpParams.getRemoteFilepath(), sftpParams.getLocalFilepath());
} catch (SftpException e) {
throw new SFTPException("Download [" + sftpParams.getRemoteFilepath() + "] from SFTP "
+ sftpConfig.getSftpHost() + ":" + sftpConfig.getSftpPort() + " error.", e);
}
}

 


(2)基於OutputStream的調用
打開一個輸出流,將遠程文件寫入輸出流中。如,通過FileOutPutStream得到一個本地文件輸出流,調用該方法將遠程文件數據寫入該輸出流,默認數據塊大小為32KB。

public void download(SFTPParams sftpParams, OutputStream os) throws SFTPException {
try {
channelSftp.get(sftpParams.getRemoteFilepath(), os);
} catch (SftpException e) {
throw new SFTPException("Download [" + sftpParams.getRemoteFilepath() + "] from SFTP "
+ sftpConfig.getSftpHost() + ":" + sftpConfig.getSftpPort() + " error.", e);
}
}


(3)基於InputStream調用
將遠程文件數據轉換成一個輸入流,之后可以通過代碼從輸入流中拿取數據

public InputStream download2InputStream(SFTPParams sftpParams) throws SFTPException {
try {
return channelSftp.get(sftpParams.getRemoteFilepath());
} catch (SftpException e) {
throw new SFTPException("Download [" + sftpParams.getRemoteFilepath() + "] from SFTP "
+ sftpConfig.getSftpHost() + ":" + sftpConfig.getSftpPort() + " error.", e);
}
}

####5、監聽器
JSch可以文件傳輸時,對傳輸進度進行監控,通過實現JSch提供的SftpProgressMonitor接口來實現監聽器的功能。
SftpProgressMonitor接口定義如下

package com.jcraft.jsch;

public interface SftpProgressMonitor{
public static final int PUT=0;
public static final int GET=1;
public static final long UNKNOWN_SIZE = -1L;

// 傳輸開始時,調用init方法。其中op為操作類型,即為上面定義的PUT/GET,max為文件的大小
void init(int op, String src, String dest, long max); 
// 當每次傳輸一個數據塊后,調用count方法,參數為這一次傳輸的數據塊大小
boolean count(long count);
// 傳輸結束時,調用end方法
void end();
}

 

現在實現一個每隔1秒,獲取上傳的進度的功能

public class TimerSFTPProgressMonitor implements SftpProgressMonitor {

private boolean isTransEnd = false; // 是否傳輸完成
private long fileTotalSize; // 需要傳輸文件的大小
private long fileTransferedSize; // 已傳輸的大小
private ScheduledExecutorService service = Executors.newScheduledThreadPool(1);

public void init(int op, String src, String dest, long max) {
System.out.println("Begin transferring.");
fileTotalSize = max;
if (fileTotalSize != 0) {
final DecimalFormat df = new DecimalFormat("#.##");
service.scheduleAtFixedRate(new Runnable() {
public void run() {
if (!isTransEnd) {
if (fileTransferedSize != fileTotalSize) {
double d = ((double) fileTransferedSize * 100) / (double) fileTotalSize;
System.out.println("Current progress: " + df.format(d) + "%");
} else {
isTransEnd = true; // 已傳輸大小等於文件總大小,則已完成
}
}
}
}, 0, 1, TimeUnit.SECONDS);
}
}

public boolean count(final long count) {
fileTransferedSize = fileTransferedSize + count;
return true;
}

public void end() {
service.shutdown();
System.out.println("End transferring, transferedSize : " + fileTransferedSize);
}

}

 

最后效果如下

 

##五、踩坑記錄
1、JSch實現sftp上傳時,2: No such file

 

原因:在SFTP服務配置一節中,我們將文件上傳到/sftp/sftpuser/upload,但是在代碼中不能直接寫入這個路徑,而需要寫入 /upload 這個路徑,因為對於sftpuser來說,它是沒有/sftp/sftpuser/這個路徑的。可以通過sftp的 ls / 命令來查看


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM