欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

在Java中实现SFTP协议文件传输的两种解决方案

程序员文章站 2024-03-24 15:22:10
...


这篇博文来聊聊在Java中实现SFTP协议文件传输的两种解决方案


1.1 背景

我们知道,XFTP 是一款非常流行的 FTP 文件传输工具。

其中 XFTP 目前支持两种文件传输协议

  • 一种是FTP
    在Java中实现SFTP协议文件传输的两种解决方案
  • 另外一种是SFTP

如图所示:
在Java中实现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://*.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.