在Java中實現SFTP協議文件傳輸的兩種解決方案
這篇博文來聊聊在Java中實現SFTP協議文件傳輸的兩種解決方案
1.1 背景
我們知道,XFTP 是一款非常流行的 FTP 文件傳輸工具。
其中 XFTP 目前支持兩種文件傳輸協議
- 一種是FTP
- 另外一種是SFTP
如圖所示:
當然除此之外還有一種不太常用的FTPS
那麼這三種協議有哪些區別呢?
文件傳輸協議類型 | 描述 |
---|---|
FTP | 基礎的文件傳輸 |
FTPS | 基於TLS 加密 |
SFTP | 基於SSH 加密 |
有時候我們可能會有需求,在我們的項目中使用FTP 文件傳輸功能。
那麼在Java中有哪些解決方案呢?
1.2 關於 FTP /FTPS
Apache Commons Net™庫實現了許多基本Internet協議的客戶端。
支持的協議如下所示:
- FTP/FTPS
- FTP over HTTP (experimental)
- NNTP
- SMTP(S)
- POP3(S)
- IMAP(S)
- Telnet
- TFTP
- Finger
- Whois
- rexec/rcmd/rlogin
- Time (rdate) and Daytime
- Echo
- Discard
- NTP/SNTP
其中這個類庫中有如下三個類:
- org.apache.commons.net.tftp.TFTPClient
支持不需要賬號密碼訪問的FTP 協議文件傳輸,不支持帶驗證的FTP文件傳輸- org.apache.commons.net.ftp.FTPClient
支持FTP,不支持FTPS和SFTP- org.apache.commons.net.ftp.FTPSClient
支持FTPS,不支持SFTP
前面兩種方式這裏不做過多討論,我們重點看下SFTP 協議文件傳輸的解決方案。
1.3 關於SFTP
關於在Java中實現SFTP協議文件傳輸有兩個庫可供使用。
- 使用 JSch 庫
- 使用sshj 庫
爲了便於測試,我這裏將賬號密碼等信息配置成靜態工具類
代碼如下所示:
/**
* @author qing-feng.zhao
*/
public class MyServerInfoConstant {
/**
* FTP IP 地址
*
*/
public static final String REMOTE_SERVER_IP="127.0.0.1";
/**
* Sftp賬號
*/
public static final String USER_NAME="root";
/**
* Sftp密碼
*/
public static final String PASS_WORD="toor";
/**
* 測試遠程服務器的文件路徑
*/
public static final String SRC_FILE_PATH="/opt/app/remote.txt";
/**
* 本地保存文件路徑
*/
public static final String TARGET_FILE_PATH="C:/test/local.txt";
/**
* 禁用構造方法
*/
private MyServerInfoConstant(){}
}
請自行修改IP 賬號和密碼爲自己FTP 服務器的賬號和密碼。
解決方案一:使用 JSch 庫
調用代碼如下:
import com.xingyun.constant.MyServerInfoConstant;
import com.xingyun.utils.SmartSftpUtils;
/**
* http://www.jcraft.com/jsch/
* @author qing-feng.zhao
*/
public class SmartSftpUtilsTest {
public static void main(String[] args) {
//FTP IP
String remoteServerIp= MyServerInfoConstant.REMOTE_SERVER_IP;
//默認是22端口
//Sftp賬號
String username=MyServerInfoConstant.USER_NAME;
//Sftp密碼
String password=MyServerInfoConstant.PASS_WORD;
//遠程服務器地址
String remoteFilePath=MyServerInfoConstant.SRC_FILE_PATH;
//本地保存文件路徑
String localFilePath=MyServerInfoConstant.TARGET_FILE_PATH;
//調用工具類下載文件
SmartSftpUtils.downloadFileBySftp(remoteServerIp,username,password,remoteFilePath,localFilePath);
}
}
工具類配置如下:
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
/**
* @sees http://www.jcraft.com/jsch/
* @author qing-fegn.zhao
*/
@Slf4j
public final class SmartSftpUtils {
/**
*
* @param remoteServerIp
* @param username
* @param password
* @param remoteFilePath
* @param localFilePath
*/
public static void downloadFileBySftp(String remoteServerIp,
String username,
String password,
String remoteFilePath,
String localFilePath){
JSch jsch = new JSch();
try {
Session session = jsch.getSession(username, remoteServerIp, 22);
session.setConfig("StrictHostKeyChecking", "no");
session.setPassword(password);
session.connect();
Channel channel = session.openChannel("sftp");
channel.connect();
ChannelSftp sftpChannel = (ChannelSftp) channel;
sftpChannel.get(remoteFilePath, localFilePath);
sftpChannel.exit();
session.disconnect();
} catch (JSchException e) {
//To change body of catch statement use File | Settings | File Templates.
log.error(e.getMessage(),e);
} catch (SftpException e) {
log.error(e.getMessage(),e);
}
}
/**
* 禁用構造方法
*/
private SmartSftpUtils(){}
}
解決方案二:使用sshj 庫
這個類庫更加強大,除了基礎的SFTP協議文件傳輸,還支持 Shell 下的各種常用命令,比如創建文件夾,列出文件目錄等。
調用方法如下:
import com.xingyun.constant.MyServerInfoConstant;
import com.xingyun.utils.SmartSshUtils;
/**
* @author
*/
public class SmartSshUtilsTest {
public static void main(String[] args) {
String hostName= MyServerInfoConstant.REMOTE_SERVER_IP;
String username=MyServerInfoConstant.USER_NAME;
String password=MyServerInfoConstant.PASS_WORD;
String srcFilePath=MyServerInfoConstant.SRC_FILE_PATH;
String targetFilePath=MyServerInfoConstant.TARGET_FILE_PATH;
SmartSshUtils.downLoadFileBySsh(hostName,username,password,srcFilePath,targetFilePath);
}
}
sshJ工具類:
import lombok.extern.slf4j.Slf4j;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.sftp.SFTPClient;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import java.io.IOException;
/**
* https://github.com/hierynomus/sshj
* @Sees https://stackoverflow.com/questions/14617/how-to-retrieve-a-file-from-a-server-via-sftp
* @author qing-feng.zhao
*/
@Slf4j
public final class SmartSshUtils {
public static void downLoadFileBySsh(String hostName,
String username,
String password,
String srcFilePath,
String targetFilePath
) {
SSHClient ssh = new SSHClient();
SFTPClient sftpClient = null;
try {
//ssh.loadKnownHosts(); to skip host verification
ssh.addHostKeyVerifier(new PromiscuousVerifier());
ssh.connect(hostName);
ssh.authPassword(username, password);
sftpClient = ssh.newSFTPClient();
sftpClient.get(srcFilePath, targetFilePath);
//create a folder
sftpClient.mkdir("/opt/app/testFolder");
//sftpClient.mkdirs("");創建多級文件夾
//sftpClient.rmdir("");重命名文件夾
//sftpClient.ls(""); //列出當前目錄
} catch (IOException e) {
log.error(e.getMessage(), e);
} finally {
if (null != sftpClient) {
try {
sftpClient.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
try {
ssh.disconnect();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
/**
* 靜態工具類應該禁用構造方法
*/
private SmartSshUtils(){}
}
貼上完整的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>SmartFtpSample</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<!-- 設置當前項目源碼使用字符編碼爲UTF-8 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 設置當前項目所需要的JDK版本 Open JDK下載地址:https://jdk.java.net/ -->
<java.version>1.8</java.version>
<!-- 設置當前項目編譯所需要的JDK版本 Open JDK下載地址:https://jdk.java.net/ -->
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<!-- 設置maven編譯插件版本,可通過下面網址查看最新的版本-->
<!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-compiler-plugin -->
<maven.compiler.plugin.version>3.5.1</maven.compiler.plugin.version>
<!-- 項目所使用第三方依賴jar包的版本,建議以後都使用這種方式,方便今後維護和升級 -->
<lombok.version>1.18.10</lombok.version>
<commons.net.version>3.6</commons.net.version>
<spring.boot.version>2.1.6.RELEASE</spring.boot.version>
<jsch.version>0.1.55</jsch.version>
<sshj.version>0.27.0</sshj.version>
</properties>
<dependencies>
<!--added lombok support -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!--added logback as log lib -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>${spring.boot.version}</version>
<scope>compile</scope>
</dependency>
<!-- added Ftp and FTPS support -->
<!-- https://mvnrepository.com/artifact/commons-net/commons-net -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>${commons.net.version}</version>
</dependency>
<!-- added XFtp support -->
<!-- https://mvnrepository.com/artifact/com.jcraft/jsch -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>${jsch.version}</version>
</dependency>
<!-- added SFTP and Shell command support -->
<dependency>
<groupId>com.hierynomus</groupId>
<artifactId>sshj</artifactId>
<version>${sshj.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--該插件限定Maven打包時所使用的版本,避免出現版本不匹配問題-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
這裏只貼出部分,關於更多其他解決方案的探討見我的Github.