在无线通信模块失效或网络完全隔离的场景下,如何完成设备间的文件传输?ShadowCat 项目给出了一种极简而可靠的答案:利用现代浏览器原生能力,通过 QR 码的光学扫描实现端到端数据传输。这一方案无需服务器中介,不依赖蓝牙、Wi-Fi 或移动网络,仅需两台设备的摄像头与浏览器即可工作。
协议设计:QRX1 分块传输格式
ShadowCat 的核心在于自定义的 QRX1 协议,它将文件切分为多个 QR 码帧循环播放,接收方通过摄像头逐帧捕获并重组。协议采用纯文本格式,以 | 作为字段分隔符 —— 这一选择经过精心考量,因为 Base64 字符集天然不包含 | 符号,避免了转义复杂性。
协议定义两种帧类型:
- Header 帧:
QRX1|H|<total>|<filename>|<sizeBytes>|<crc32hex> - Data 帧:
QRX1|D|<idx>|<base64chunk>
Header 帧在传输开始时发送,携带文件元数据与 CRC32 校验值;Data 帧按 1-based 索引编号,承载 Base64 编码的文件分块。接收方通过索引追踪已收到的块,自动忽略重复帧,最终按序号拼接完成文件重组。
工程参数:吞吐量与可靠性的平衡
QR 码的容量受版本与纠错级别制约。ShadowCat 默认配置为每帧 500 字符、3 FPS 刷新率、M 级纠错(15% 容错),在此设定下可实现约 0.83 KB/s 的有效吞吐。以 100 KB 文件为例,完整传输一轮约需 2 分钟,考虑到帧丢失与重复捕获,实际接收通常需要 1-2 轮循环。
| 参数项 | 默认值 | 可调范围 | 影响说明 |
|---|---|---|---|
| Chunk 大小 | 500 字符 | 50-2000 | 越大单帧数据越多,但 QR 码密度增加导致扫描失败率上升 |
| 刷新率 (FPS) | 3 | 1-20 | 过高会导致旧设备摄像头无法清晰捕获 |
| 纠错级别 | M (15%) | L/M/Q/H | Q 级 (25%) 适合光线不佳或老旧设备,但会减少有效载荷 |
当遇到 "code length overflow" 渲染错误时,应优先降低 Chunk 大小或下调纠错级别。对于老旧手机,建议将 Chunk 缩减至 300 字符、FPS 降至 1-2、ECC 提升至 Q 级,以换取更高的扫描成功率。
部署要点:HTTPS 与本地服务
由于浏览器安全策略,getUserMedia API 仅在 HTTPS 或 localhost 环境下可用,直接通过 file:// 协议打开 HTML 文件将无法调用摄像头。最简单的本地部署方式是使用 Python 内置服务器:
python3 -m http.server 8000
随后通过局域网 IP 访问 http://<host>:8000/shadowcat.html 即可。若需在 iOS Safari 跨设备访问,则必须配置 HTTPS,可使用 Caddy 自动签发本地证书或部署自签名证书。
接收端机制:去重与可视化追踪
接收端的实现体现了容错设计的精髓。除了基础的帧解析与 Base64 解码外,系统维护一个布尔数组标记已接收的块索引,并在 UI 中以网格形式实时展示 —— 绿色表示已捕获,灰色表示待接收。这种可视化反馈让用户能够直观判断传输进度,并在出现丢帧时保持设备对准。
文件重组完成后,系统执行双重校验:首先比对实际字节数与 Header 声明的 size,其次计算 CRC32 并与 Header 中的校验值比对。任一检查失败都会在下载按钮旁显示红色警告,防止用户获取损坏文件。
应用场景与局限性
ShadowCat 的设计初衷是解决极端场景下的文件传输需求:无线电模块损坏的旧手机、完全离线的封闭环境、或对无线信号有严格管控的保密场所。其单文件 HTML 的形态也使其具备极强的便携性 —— 可通过邮件附件、U 盘或局域网共享直接分发。
然而,光学传输的物理特性决定了其局限性。0.83 KB/s 的速率意味着传输 1 MB 文件需约 20 分钟,这限制了其在大文件场景的应用。此外,环境光线、摄像头对焦速度、手持稳定性都会影响传输效率。对于需要频繁传输的场景,仍建议优先使用传统无线方案。
技术实现要点
ShadowCat 将 qrcode-generator 与 jsQR 两个开源库内嵌至单文件中,实现零依赖运行。编码端通过 Canvas 2D 上下文绘制 QR 码矩阵,支持动态调整单元格大小与边距;解码端利用 requestAnimationFrame 持续读取视频帧,通过 jsQR 解析图像数据中的 QR 码内容。
对于开发者而言,这一项目展示了浏览器原生 API 在离线场景下的潜力:FileReader 读取本地文件、Canvas 生成图像、getUserMedia 捕获视频、Blob 与 URL.createObjectURL 实现文件下载 —— 无需任何后端服务即可完成完整的文件传输链路。
资料来源
- ShadowCat GitHub 仓库: https://github.com/unprovable/ShadowCat
- 项目 README 与源码解析
内容声明:本文无广告投放、无付费植入。
如有事实性问题,欢迎发送勘误至 i@hotdrydog.com。