WebShell中实现sz/rz上传下载

最近支持上传下载,做了lrzsz的调研开发,这里总结下

使用说明

首先了解下szrz功能

  1. sz支持多文件下载

  2. rz支持多文件上传,上传目标目录为当前命中执行目录,

  3. rz不支持文件夹上传

  4. rz上传针对已存在文件,会有标记信息说明

  5. 针对体积大小,无法传输大于 4G 的文件

  6. 下载中/上传中支持取消

szrz系统支持

  1. 服务器默认是没有安装 lrzsz 工具的,需要手动安装
  2. 不是所有工具都支持 rz 与 sz,必须支持 ZModem 协议才行,安装脚本如下
1
2
3
4
5
6
7
8
9
10
# MacOS
brew install lrzsz
brew remove lrzsz

# CentOS
yum install lrzsz
yum remove lrzsz

## 跨平台安装脚本
sudo sh -c "$(curl -fsSL https://gist.githubusercontent.com/alanhg/6a299b815f4dd3d242abc16b8be6b861/raw/dbe1497208f1d968ed8b67cad09c596e35c5be9c/install-package-lrzsz.sh)"

官方demo

zmodem.js作者提供了demo,方便了解。为了保证demo正常运行,需要以下操作

  1. demo地址:https://github.com/FGasper/xterm.js/tree/zmodem
  2. node版本切换到v8重新执行npm i
  3. 启动后访问http://127.0.0.1:3000/

功能实现

上述作者的demo比较简单,并未提及一些功能,这里贴下关键实现。完整例子戳这里

sz取消下载

上传直接唤起的资源管理器选择框,在取消后,JS层面无法得知,因此只能用户主观再按ctrl c取消,之后发送abort命令进行取消。

1
activeZsession._skip();

除了上述方法外,还有个办法就是自行增加上传弹窗,让用户点击时再调用abort即可。

rz取消上传

1
2
// 停止发送数据块,xfer.send(chunk)
await zsession.close()

上传-文件已存在

xfer识别为空即表示服务端拒绝上传,目前发现原因是文件存在,不排除有其它可能性。

1
2
3
4
const xfer = await zsession.send_offer(curb);
if(!xfer){
showMessage(`${xfer.get_details().name} rejected.`);
}

进度打印

  1. 针对上传,根据已发送大小/总大小计算进度,针对下载则是接收的大小/总大小。

  2. 上传进度,循环直接打印进度会不work,原因是xterm并非同步打印,为了解决该问题,增加delay即可。

    1
    await new Promise(resolve => window.setTimeout(resolve, 0));
  3. 考虑到体验,可以除了进度之外显示下进度条, xterm.write时候不要\n换行显示即可。

1
2
3
4
5
6
7
8
9
10
function getProgressBar(total, current) {
if (total < current) {
throw new Error('total must be greater than current');
}
const progressBarLength = 40; // 进度条的长度
const progress = Math.floor((current / total) * progressBarLength);
const empty = progressBarLength - progress;
const progressBar = '█'.repeat(progress) + '░'.repeat(empty);
return `(${progressBar}) ${((current / total) * 100).toFixed(2)}%`;
}

有价值的资料

  1. https://juejin.cn/post/6935621453400244260
  2. https://wsgzao.github.io/post/lrzsz/
  3. https://qa.1r1g.com/sf/ask/672770031/