Files

182 lines
7.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TransferSu - 统一文件传输工具
基于 rsync / rclone / pv+tar 的统一拷贝管理工具,CLI/TUI 双模式,实时日志轮转,
专为"读卡器掉卡"等不稳定传输场景设计。
## 特性
- **多后端**: rsync(推荐, 断点续传)/ rclone(并行)/ pv+tar(零依赖)
- **CLI/TUI 双模式**: 未显式 `--mode cli` 时默认 TUI 启动
- **TUI 四栏布局**:
- 上栏: 总进度条(字节) + 单文件进度条(文件计数)
- 中栏: 速度曲线可视化(当前/峰值/平均速度实时文字)
- 下栏: 文件日志 + 错误日志 双面板
- 底部: 交互输入栏(空启动设定任务 / 传输控制命令)
- **进度轮询机制**: 每秒主动统计目标目录大小驱动进度,
绕过后端输出缓冲(Android 上 stdbuf/LD_PRELOAD 无效,rsync 输出全缓冲不可靠)
- **掉卡自愈**: `--retry -1` 无限自动补传 + 完成后 `--verify` 校验
- **实时日志**: 写入本地目录, 滚动保留最近 N 个(默认 20), 纳秒时间戳排序
## 安装
```bash
pip install -i https://mirrors.aliyun.com/pypi/simple/ pytest textual
```
## 用法
```bash
# TUI 模式 (默认): 空启动后在底部输入栏设定任务
python3 transfer.py
# TUI 模式带预设任务
python3 transfer.py /src /dst --bwlimit 30000
# CLI 模式 (显式指定)
python3 transfer.py --mode cli /src/ /dst/ --backend rclone --bwlimit 8M
```
### TUI 输入栏命令
```
/src /dst --bwlimit 30000 --retry -1 --verify # 启动传输任务
stop # 停止当前任务 (SIGTERM, 跳过补传)
pause # 暂停 (SIGSTOP, 轮询同步挂起)
continue # 恢复暂停 (SIGCONT)
help / clear / quit
```
进度显示:
- 总进度条: 已传字节/源总量(每秒轮询刷新)
- 单文件进度条: **文件计数**(目标文件数/源文件总数, 每 3 秒刷新)
- 速度曲线栏: 当前/峰值/平均速度文字 + Sparkline 曲线
## 参数
| 参数 | 默认 | 说明 |
|---|---|---|
| `--backend` | rsync | rsync / rclone / tar |
| `--bwlimit` | 30000 | 限速 (rsync: KB/s, rclone: 如 8M) |
| `--transfers` | 4 | rclone 并行传输数 |
| `--resume` / `--no-resume` | 开 | rsync 断点续传 |
| `--checksum` | 关 | rsync 校验模式 |
| `--verify` | 关 | 传输完成后校验(等级由 `--verify-level` 决定) |
| `--verify-level` | slow | 校验等级: ultrafast/fast/normal/slow/full |
| `--check-only` | 关 | 纯校验模式: 不传输, 仅按等级校验源/目标 |
| `--repair` | 关 | 校验发现问题后提示并重传问题文件(先删目标避免增量盲区) |
| `--retry` | 0 | 失败自动补传轮数, **-1 = 无限重试** |
| `--smart-resume` | 关 | 智能续传: 校验目标最新N个文件, 损坏删除后从断点续传 |
| `--smart-check` | 5 | 智能续传校验文件数 (<=100MB 全校验, 更大只查大小) |
| `--policy` | overwrite | 冲突策略: overwrite / skip / ask |
| `--mode` | tui | cli / tui |
| `--keep-logs` | 20 | 日志滚动保留数 |
| `--logdir` | ~/logs | 日志目录 |
## 掉卡场景最佳实践
读卡器/移动硬盘在安卓上高速写入可能 USB 掉线(如 GL3224 芯片)。
完整方案:
1. **守护脚本**自动恢复设备(掉线后重枚举):
```bash
# 部署 (脚本在项目内 usb_guard.sh, 目标设备通用 VID/PID 可在脚本头部修改)
su -c 'cp ~/Proj/TransferSu/usb_guard.sh /data/local/tmp/ && chmod 755 /data/local/tmp/usb_guard.sh'
# 启动
su -c 'setsid sh /data/local/tmp/usb_guard.sh </dev/null >/dev/null 2>&1 &'
```
守护脚本功能: ① 应用稳定性调优参数 (SCSI 超时 120s 等)
② 三种掉线场景自动重枚举恢复 (dmesg 检测 / 异常断开 / 健康探测)。
默认绑定 GL3224 读卡器 (VID:PID 05e3:0747), 换设备改脚本头部变量。
开机自启: `~/.termux/boot/boot_usb_guard.sh` (Termux:Boot 插件)。
2. **限速传输 + 无限补传 + 校验**:
```
/src/ /dst/ --bwlimit 30000 --retry -1 --verify
```
3. 掉卡时: 守护恢复设备 → TransferSu 自动重跑补传(`--partial` 只补缺失)→
直到成功 → `rsync -c` 全量校验(0 差异 = 源目标完全一致)
**智能续传(`--smart-resume`)**: 中断后重跑时,只校验目标**最新 N 个文件**
(`--smart-check N`,默认 5;≤100MB 做 md5 全校验,更大只查大小),
发现损坏立即删除该文件并从断点续传——避免对已传的 100+GB 全量重扫。
校验过程有**进度条**(总进度条复用)+ **单文件日志**(`校验 i/N: 文件名 ✓/✘ 损坏`),
N 调大(如 50)也能实时看到校验进展。
### 校验等级(`--verify-level`)
| 等级 | 校验内容 | 188GB 耗时 | 适用 |
|---|---|---|---|
| `ultrafast` | 仅文件大小 | 秒级 | 快速健康检查 |
| `fast` | 大小+mtime | ~1 分钟 | 常规检查 |
| `normal` | 大小+mtime+最新N个md5 | 分钟级 | 兼顾速度与深度 |
| `slow` | 全部文件 md5 | 40-60 分钟 | 全量可靠(默认) |
| `full` | 全部md5+文件数对比 | 最慢 | 终极确认 |
**纯校验模式(`--check-only`)**: 不传输,只按等级校验源/目标一致性,
用于传输中断后快速确认已传数据是否完好:
```bash
# 快速检查已传数据(秒级)
python3 transfer.py --mode cli --check-only --verify-level ultrafast /src/ /dst/
# 全量校验已传数据(全部 md5)
python3 transfer.py --mode cli --check-only --verify-level slow /src/ /dst/
# 校验发现问题自动提示重传(--repair)
python3 transfer.py --mode cli --check-only --verify-level slow --repair /src/ /dst/
```
> `--repair`: 校验发现问题文件后提示 [y/N],确认后用 rsync 重传问题文件。
> 重传前**先删除目标问题文件**——避免 rsync 按 size+mtime 判定相同而跳过
> (内容损坏但大小/时间戳一致的场景,增量检测有盲区)。
> 重传完成后**自动复核**:用前置校验等级(`--verify-level`)对重传文件
> 再校验一遍,确认修复成功(日志记录 `复核通过 X 个 / 失败 Y 个`)。
> rsync 原子写(临时文件+rename)保证掉卡不会产生半截损坏的正式文件,
> 残留临时文件由补传后的 `--verify` 流程发现处理。
## 日志
- 目录: `--logdir` 指定(默认 `~/logs`)
- 文件名: `{时间戳}_{后端}_{源名}.log`(时间戳开头, 排序即时间序)
- 滚动: 超过 `--keep-logs` 个自动删除最旧
- 内容: 命令/源大小/补传轮次/退出码(含帧数)/校验结果/错误行
## 冲突策略(覆盖/跳过)
| 参数 | 行为 |
|---|---|
| `--overwrite` | 覆盖已存在文件(默认) |
| `--skip` | 跳过已存在文件(`--ignore-existing`) |
| `--ask` | 逐项询问(扫描冲突后交互) |
**ask 交互选项**(CLI 与 TUI 一致):
```
y = 覆盖当前项 n = 跳过当前项
a = 全部覆盖 s = 全部跳过
q = 中止传输
```
- **CLI**: 预扫描后逐项提示
- **TUI**: 扫描完成后在底部输入栏逐项交互
- **仅当前/后续同步**: y/n 只作用当前文件; a/s 一次决定后续(已明确决策的保留)
- rsync 覆盖场景自动禁用 `--append`(追加模式阻止重写), 保留 `--partial`
- rclone 的 `--ask` 用原生 `--interactive`(y/n/s); tar 后端不支持 ask/skip
```bash
# CLI 示例
python3 transfer.py --mode cli --policy ask --retry 5 --verify /src/ /dst/
python3 transfer.py --mode cli --retry -1 --verify /src/ /dst/
# TUI 输入栏示例
/src /dst --policy ask --retry 5 --verify
/src /dst --retry -1 --verify
```
## 测试
```bash
python3 -m pytest tests/ -q
```
54 个单元测试: 解析器 / 日志轮转 / 参数解析 / 后端命令 / 冲突决策 /
目录轮询 / 暂停信号 / 校验命令。