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

技术宅星云 CSDN认证博客专家 Java MySQL Redis
技术宅星云(网名),英文名fairy,先后曾在惠普,北京中国航信工作, 目前担任北京蛙跳科技有限公司后端高级开发工程师,负责公司短视频App应用后台,擅长JAVA后端技术,CSDN博客专家。
©️2020 CSDN 皮肤主题: 终极编程指南 设计师:CSDN官方博客 返回首页
实付 59.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值