原创性声明:本文完全为笔者原创,请尊重笔者劳动力。转载务必注明原文地址。
sftp与ftp
要谈sftp
(SSH File Transfer Protocol
),首先要谈ftp
(File Transfer Protocol
),大家都知道ftp是文件传输协议,它基于tcp
协议,可以用来发送文件。刚开始学web开发的时候,接触到一些免费的云空间,当时就是用的一个ftp
工具把项目传上去的。
sftp与ssh
那sftp
,就是安全(security)的ftp
,因为它是基于ssh
协议,ssh
是一个安全协议,用来在不同系统或者服务器之间进行安全连接,ssh
在连接和传送的过程中会加密所有的数据。所以通俗的来讲,通过ssh协议进行文件传输,那就是sftp。
那么如何使用ssh
来实现文件传输呢?熟悉linux的伙伴们应该对ssh
也不陌生,因为linux自带了ssh(嘘!其实我并不熟悉linux,只是知道linux自带了ssh,以前装了没多久就卸了,现在发现会linux逼格会提高不少。一定要再装一个玩一下!)。
遗憾的是,ssh基本上是基于linux和一些客户端安装软件。那么在我们平常的web开发中,要用sftp来传输文件怎么办呢?jsch就是解决办法了。
jsch简介
jsch
是ssh的纯java实现。这么讲有点抽象,通俗说,你在官网上down下来就是一个jar包,引入你的项目,就可以用来给一个同样开启了ssh服务的服务器安全的传文件了(当然,你需要那台目标服务器的一些用户名和密码信息,不然就gg了)。
开始使用
- 第一步:首先在maven中央仓库中查一下怎么在pom中依赖,可以点这里。
tip: 如果你用的是
Gradle
等其它构建工具,就用其他方式依赖进项目。如果没用构件工具,直接把jar包添加到项目里吧。
maven的是这个(我用的是当前最新版本0.1.54):
<!-- https://mvnrepository.com/artifact/com.jcraft/jsch -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>
加到pom文件中就可以进行下一步了。
- 第二步:创建一个工具类:SFTPUtils.java, 内容如下
import com.jcraft.jsch.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Properties;
/**
* SFTP工具类
* 包含两个方法:
* 一个创建一个sftp通道对象,并返回这个对象,通过这 个对象可以直接发送文件。
* 另一个是用于关闭回话和通道的。
*/
public class SFTPUtils {
static private final Logger log = LoggerFactory.getLogger(SFTPUtils.class);
static private Session session = null;
static private Channel channel = null;
static private int timeout = 60000; //超时数,一分钟
/**
* 传入一个通道对象
* @param username 远程要连接的服务器的用户名
* @param password 远程要连接的服务器的密码
* @param ip 远程服务器ip
* @param port 远程服务器的ssh服务端口
* @return ChannelSftp返回指向这个通道指定的地址的channel实例
* @throws JSchException
*/
public static ChannelSftp getChannel(String username, String password, String ip, String port) throws JSchException {
JSch jsch = new JSch(); // 创建JSch对象
// 根据用户名,主机ip,端口获取一个Session对象
session = jsch.getSession(username, ip, Integer.valueOf(aisle.getServerPort()));
log.info("Session created...");
if (password != null) {
session.setPassword(password); // 设置密码
}
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config); // 为Session对象设置properties
session.setTimeout(timeout); // 设置timeout时间
session.connect(); // 通过Session建立链接
log.info("Session connected, Opening Channel...");
channel = session.openChannel("sftp"); // 打开SFTP通道
channel.connect(); // 建立SFTP通道的连接
log.info("Connected successfully to ip :{}, ftpUsername is :{}, return :{}",
ip,username, channel);
return (ChannelSftp) channel;
}
/**
* 关闭channel和session
* @throws Exception
*/
public static void closeChannel() throws Exception {
if (channel != null) {
channel.disconnect();
}
if (session != null) {
session.disconnect();
}
}
}
- 第三步:工具类都建好了,就直接用吧——创建测试类。
import com.gildata.gup.util.SFTPUtils;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.SftpException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.FileInputStream;
import java.util.Vector;
/**
* sftp推送测试类
*/
@Service
public class SftpTest {
private final Logger log = LoggerFactory.getLogger(SftpPushTest.class);
/**
* 文件推送的测试方法。
* destDirPath 远程服务器要保存的文件夹路径
* file 本地要推送的文件对象
* username 远程服务器的用户名
* password 远程服务器的密码
* ip 远程服务器ip
* port 远程服务器ssh服务端口
*/
public void testSftp() throws SftpException {
// 假设参数值
String dstDirPath = "E:\\target";
String username = "admin";
String password = "admin";
String ip = "127.0.0.1";
String port = 21;
ChannelSftp channelSftp = null;
String dstFilePath; // 目标文件名(带路径),如: D:\\file\\file.doc,这个路径应该是远程目标服务器下要保存的路径
try {
// 一、 获取channelSftp对象
channelSftp = SFTPUtils.getChannel(username, password, ip, port);
// 二、 判断远程路径dstDirPath是否存在(通道配置的路径)
try {
Vector dir = channelSftp.ls(dstDirPath);
if (dir == null) { // 如果路径不存在,则创建
channelSftp.mkdir(dstDirPath);
}
} catch (SftpException e) { // 如果dstDirPath不存在,则会报错,此时捕获异常并创建dstDirPath路径
channelSftp.mkdir(dstDirPath); // 此时创建路o如果再报错,即创建失败,则抛出异常
e.printStackTrace();
}
// 三、 推送文件
try {
log.info("send the file : {}", file.getName());
dstFilePath = dstDirPath + "\\" + file.getName();
log.info("the file all path is :{}", dstFilePath);
// 推送: dstFilePath——传送过去的文件路径(全路径),采用默认的覆盖式推送
channelSftp.put(new FileInputStream(file), dstFilePath); // jsch触发推送操作的方法
} catch (SftpException e) {
log.debug("An error occurred during sftp push, send data fail, the target path is :{}", dstDirPath);
if (log.isDebugEnabled()) {
e.printStackTrace();
}
}
} finally {
// 处理后事
if (channelSftp != null)
channelSftp.quit();
try {
SFTPUtils.closeChannel();
} catch (Exception e) {
if (log.isDebugEnabled())
e.printStackTrace();
}
}
}
}
执行testSftp
方法,就可以把file文件传到目标服务器的dstDirPath目录下了。
假设file文件在本地的路径为: D:\\source\\sftp_learning.ppt
。而目标路径dstDirPath为: E:\\target
,那么执行推送后,将会在ip为ip
的远程设备下的E:\\target
目录下找到sftp_learning.ppt
文件。
问题?!
不过遗憾的是,window并不像linux一样自带了ssh服务。像上面的E:\\target
这样的目录显然表明了这个远程设备是window系统。正常开发中,即使你的用户名、 密码、 端口都没有输错,程序也将会抛SftpException异常,那是因为你得目标服务器没有启用ssh服务。
怎么解决呢?
既然目标服务器是没有自带ssh服务的window,那就想办法在window下配置ssh服务咯。
一般而言,服务器通常跑在linux下,所以不用担心这个问题。笔者这次也是因为想在自己的window下本地测试一下,所以遇到了这个问题。如何在window下配置ssh服务,这又是另一个话题了。这次测试中,我用的是Cygwin
工具。具体怎么使用,网上一搜一大把。如果读着支持笔者,就请关注我吧,我会尽快把Cygwin
的使用心得分享给大家的!
关于jsch实现sftp传输文件的分享就这么些了。希望对大家有帮助!(正好22点22分,OMG)
更多关于ChannelSftp的API方法可以参见官网,或者百度一下。就put方法就有很多重载。还支持进度显示。