在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.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章