ssh2-sftp-client使用介绍
最近在做WebShell,除了sz/rz命令方式实现上传下载之外,需要GUI方式实现基本的文件操作,比如拉取文件列表,上传下载。
调研后决定使用基于sftp/ssh实现的ssh2-sftp-client。
这里总结下使用中遇到的问题
服务禁用/开启设定
每个Linux机器默认都有SFTP服务,毕竟底层是SSH协议,算是标配,但用户可以通过在服务端设置来选择开启和关闭sftp服务的。
因此在实际开发中要考虑到服务不可用场景下的处理
1 | vi /etc/ssh/sshd_config |
如果服务没有正常开启,则异常错误码是ERR_GENERIC_CLIENT
list
如果是想拉取文件列表,使用的是list方法,有几个问题需要注意
不支持
~
remotePath路径必须是相对或者绝对路径,不可以是路径别名,比如
~
1
2
3
4
5
6
7
8
9
10async function main() {
try {
await sftp.connect(config);
let fileList = await sftp.list(remotePath);
console.log(fileList);
await sftp.end();
} catch (e) {
console.error(e);
}
}文件type
返回结果中有type字段,表示文件类型,其中
d
是文件夹-
是文件l
是软链接
针对链接类型文件,想知道具体是文件还是文件夹,只能再去单独查询判断,比如使用stat
方法,返回值中isDirectory,isFile可以明确具体类别
size单位为
字节
- 这点与一般shell命令显示单位一致
- 文件夹类型也会返回size大小
rights权限
通过list接口可以拿到文件所属user/group,同时文件针对所属user/group/other权限,rights与longname信息等价,只是rights将权限信息结构化显示。owner字段表示归属userID,group表示归属groupID。通过信息还是无法直接判断当前用户对该文件是否有读写权限,为了判断需要SSH单独执行
id username
命令来获取当前用户userID和groupID举个例子如下,注意SSH2Client并不是sftpClient,这个需要使用ssh2下的client单独连接,sftp包不支持
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18const {Client: SSH2Client} = require('ssh2');
const ssh2Client = new SSH2Client();
await new Promise(resolve => ssh2Client.connect(config).on('ready', () => {
return ssh2Client.exec(`id ${config.username}`, (err, stream) => {
stream.on('data', (buf) => {
const idRes = buf.toString();
console.log(idRes); // uid=0(root) gid=0(root) groups=0(root)
const rights = idRes.match(/\d+/g);
console.log({
uid: +rights[0],
gid: +rights[1],
});
return resolve(idRes);
});
})
}));当拿到权限信息后,结合list接口返回的信息即可判断。
1
2
3
4
5const rights = idRes.match(/\d+/g);
console.log({
uid: +rights[0],
gid: +rights[1],
});
filter
list方法list(path, filter) ==> Array[object]
第二个参数filter可以对列表文件进行过滤。比如隐藏文件不显示^[^.]
,但注意Windows文件名并没有规范要求一定是.开头文件,因此Windows下无法通过该方法过滤隐藏文件的显s。解决办法是比如通过执行命令获取文件隐藏属性进一步确定。
返回数据结构
1 | { |
put/fastPut
上传文件时可以使用这两个方法,区别主要在于是否支持流。比如我这里的设计是用户从网页发送的请求是走了node服务,node服务再发起SSH连接到目标机器,如果使用fastPut方法就一定存在临时文件,这样第一是速度慢,且实现上传进度的话,也并不准确了,毕竟是先分片上传到服务端,然后再分片上传到服务器,两个过程是割裂的。因此像我这里的场景,更适合使用put方法。
put支持的流写法确保是可读流即可
这里举个例子是网页发起的WS传输数据构造的可读流,这样前端发出的数据片,直接流化发送到目标机器,本身在node服务端不做任何其它处理
上传文件默认会有权限,这里建议设置为
0o644
,该值参考的FileZilla-SFTP上传后给予的权限设定。- 644即
rw-r--r--
,用户自己有读写权限,组/其他只有读权限
- 644即
get/fastGet
与上传类似,如果需要中转到前台,可以使用get方法,构建可写流。
限速
有时为了安全,需要对上传下载进行限速,做法如下。注意,限速需要流化
1 | const Throttle = require('throttle'); |
取消
在上传下载过程中,突然想取消了,做法是直接断开SFTP连接,然后重新连接。
断点下载/上传
针对断点下载,远程文件可读流直接控制起始位置即可。
1 | sftp.get(remoteFile, fileWtr, { |
针对断点上传,可以走append方法即可。
downloadDir/uploadDir
上下载文件夹可以使用该方法,但缺点明显,比如无法流化,这样比如存在WebShell服务端中转,如果使用该方法,中转文件就必然存在,想要的上下载进度就无法准确。
因此可以使用get/put来解决,本质就是递归调用来实现文件夹中所有文件的读写操作,其中创建文件夹的话可以走mkdir解决。
SFTP协议介绍
https://www.ssh.com/academy/ssh/sftp
写在最后
done