# 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 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 个单元测试: 解析器 / 日志轮转 / 参数解析 / 后端命令 / 冲突决策 / 目录轮询 / 暂停信号 / 校验命令。