sftp

简介

SFTP是SSH的一部分,在SSH软件包中,已包含SFTP的安全文件信息传输子系统,它必须使用sshd守护进程(端口号默认是22)来完成相应的连接和答复操作。

类别 SFTP FTP
是否额外安装 不需要。linux默认安装ssh,有SSH即有SFTP Linux默认不提供ftp,需要手动安装FTP服务器
安全 更高。在ftp的基础上加密传输认证信息、数据。需要用户名密码登录,也需要密钥加密。 比sftp略差。有可能要求基于密码的安全验证。
端口 通过SSH协议(TCP端口22)建立连接 通过TCP端口21上的控制连接建立连接
协议 Secure File Transfer Protocol,SSH加密的文件传输协议 File Transfer Protocol,文件传输协议
效率 传输效率比普通的FTP要低 较高

配置

用户权限配置

修改sftp配置的目的是:限制【特定用户】只能在【特定目录】的访问。

1
比如:用户UserA,只能读取/data/usera,可以上传文件到/data/usera/upload。

java程序访问

依赖

使用第三方工具jsch包中的ChannelSftp类,来访问SFTP。

  1. 通过Ip、Port、Username、Password获取一个Session
  2. 通过Session打开SFTP通道(获得SFTP Channel对象)
  3. 再建立通道(Channel)连接
  4. 通过Channel对象来调用SFTP的各种操作方法
  5. 操作完SFTP需要手动断开Channel连接与Session连接
1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/com.jcraft/jsch -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>

常见API

配置

1
2
3
4
5
6
7
8
9
cms.file:
ip:
192.168.145.128
usename:
root
password:
root
path:
/user/wlw

工具文件

调用

1
2
3
4
@Autowired
private CommonSFtpFileService commonSFtpFileService;

commonSFtpFileService.uploadFile(inputStream, "test.csv");

工具文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
package com.example.demoinit.sftp;

import com.jcraft.jsch.*;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Properties;

@Service
public class CommonSFtpFileService {

@Value("${cms.file.ip}")
private String destinationIp;

@Value("${cms.file.port:22}")
private int destinationPort;

@Value("${cms.file.usename}")
private String userName;

@Value("${cms.file.password}")
private String password;

@Value("${cms.file.path}")
private String destinationPath;

@Value("${cms.file.connect.time:300000}")
private int connectTime;

private ChannelSftp sftp;
private Session session;

private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(CommonSFtpFileService.class);

/**
* SFTP密码方式登录验证
*/
@SuppressWarnings("boxing")

public void login() {

LOGGER.info("sftp connect by ip:{},port:{},userName:{},password:{}", destinationIp, destinationPort, userName,
password);

try {
final JSch jsch = new JSch();
session = jsch.getSession(userName, destinationIp, destinationPort);
if (session == null) {
LOGGER.info("登录参数不正确");
}
//密码验证方式
session.setPassword(password);

final Properties linkConfig = new Properties();
//设置第一次登陆的时候提示,可选值:(ask | yes | no)
linkConfig.put("StrictHostKeyChecking", "no");
session.setConfig(linkConfig);
//设置超时时间
session.connect(connectTime);

final Channel channel = session.openChannel("sftp");
channel.connect();

sftp = (ChannelSftp) channel;
} catch (final JSchException e) {
LOGGER.warn("Cannot connect to specified sftp server : {}:{} Exception message is: {}", destinationIp,
destinationPort, e.getMessage());
}

}

/**
* 登出
*/
public void logout() {

if (sftp != null) {
if (sftp.isConnected()) {
sftp.disconnect();
}
}
if (session != null) {
if (session.isConnected()) {
session.disconnect();
}
}
}

/**
* 以文件流方式上传附件
*
*/
public void uploadFile(final InputStream sourceFileInputStream, final String sftpFileName) {

//登录验证
this.login();
try {
//切换到指定目录
sftp.cd(destinationPath);
} catch (final SftpException e) {
LOGGER.warn("directory is not exist");
try {
sftp.mkdir(destinationPath);
sftp.cd(destinationPath);
} catch (final SftpException e1) {
LOGGER.warn("directory build error!");
}
}

try {
sftp.put(sourceFileInputStream, sftpFileName);
} catch (final SftpException e) {
LOGGER.warn("file:{} is upload error!", sftpFileName);
} finally {
closeStream(sourceFileInputStream, null);
}
LOGGER.info("file:{} is upload successful", sftpFileName);
}

/**
* 删除指定文件
*/
public void deleteFile(final String sftpFileName) {

//登录验证
this.login();

try {
sftp.cd(destinationPath);
sftp.rm(sftpFileName);
} catch (final SftpException e) {
LOGGER.warn("file:{} is delete error!", sftpFileName);
} finally {
closeStream(null, null);
}
}

/**
* 下载FTP服务器上的文件
*/
@SuppressWarnings("resource")
public void downloadFile(final String sftpFileName, final HttpServletRequest request,
final HttpServletResponse res) {

//登录验证
this.login();

InputStream ins = null;

OutputStream out = null;
try {
sftp.cd(destinationPath);
ins = sftp.get(sftpFileName);

//设置下载格式
String fileName = null;
out = new BufferedOutputStream(res.getOutputStream());
// response响应信息设置
res.setContentType("application/octet-stream;charset=UTF-8");
// 不同浏览器中文乱码问题
final String userAgent = request.getHeader("user-agent").toLowerCase();
if (userAgent.contains("msie") || userAgent.contains("like gecko")) {
fileName = URLEncoder.encode(sftpFileName, "UTF-8");
} else {
fileName = new String(sftpFileName.getBytes("UTF-8"), "iso-8859-1");
}
res.setHeader("Content-Disposition", "attachment;filename=" + fileName);
res.setCharacterEncoding("UTF-8");
FileCopyUtils.copy(ins, out);
out.flush();

} catch (final Exception e) {
LOGGER.error("FILE_DOWNLOAD_EXCEPTION:" + e.getMessage(), e);
} finally {
closeStream(ins, out);
}
}

/**
* 关闭资源
*
* @param in
* @param out
*/
private void closeStream(final InputStream in, final OutputStream out) {

if (in != null) {
IOUtils.closeQuietly(in);
}
if (out != null) {
IOUtils.closeQuietly(out);
}
this.logout();

}

}
-------------Keep It Simple Stupid-------------
0%