目录

ShadowCat:用 QR 码在浏览器间离线传输文件

ShadowCat:用 QR 码在浏览器间离线传输文件

有时候,两台设备之间能用来通信的只剩下摄像头——比如一部老手机的射频模块(BLE、NFC)早已损坏,但摄像头和浏览器还能用。ShadowCat 解决的正是这个问题:在一个纯 HTML 页面里,把任意文件编码成一连串 QR 码,供另一台设备扫码接收。整个过程不需要网络、不需要蓝牙、不需要 NFC,只要两个浏览器和两张能看见对方的屏幕。

核心判断

ShadowCat 的价值不在"快",在"通"。在所有无线协议都不可用的场景下,它是唯一可用的选项。协议设计得很克制:header + 顺序数据块的线性结构,换来了接收端极简的追踪逻辑;Base64 编码避免了自定义字符集,直接用 | 分隔符做解析。整个方案可以总结为一句话——把文件切碎,用 QR 码一张一张地发,收端自己拼回去。

系统地图

发送端                        接收端
───────────────────────────  ───────────────────────────
Encode file to Base64   ──►  Camera scan QR frames
Split into chunks       ──►  Parse QRX1|D|<idx>|<data>
Loop: [header, chunk1…N]     Track received chunk indices
  @ chosen FPS               Show missing-chunks grid
  @ chosen chunk size        Request resend of missing frames
  @ chosen ECC level         On complete: verify CRC → Download

协议只有两种帧类型,header 携带文件元信息,数据帧按顺序编号,接收端独立追踪每块的到达状态。

协议设计

帧格式

ShadowCat 定义了两种 QR 帧格式:

Header 帧(发送时只发一次):

QRX1|H|<total>|<filename>|<sizeBytes>|<crc32hex>
  • total:本次传输的总帧数
  • filename:原始文件名
  • sizeBytes:文件原始大小(字节)
  • crc32hex:整个文件的 CRC-32 校验值(十六进制),用于最终校验

Data 帧(循环发送):

QRX1|D|<idx>|<base64chunk>
  • idx:帧序号,从 1 开始
  • base64chunk:对原始文件分块后的 Base64 编码片段

Base64 字符集中不包含 |,所以解析逻辑极为简单——split('|') 即可完成全部解析工作。这也是协议设计的一个小巧之处:用 Base64 自带的字符边界替代了自定义的块边界协议。

接收端逻辑

接收端的核心是一个缺失块追踪器。流程如下:

  1. 扫描到 header 帧后,提取总帧数和 CRC,开始追踪
  2. 每收到一个数据帧,按 idx 记录,重复帧直接忽略(已收到的帧不会触发任何状态变化)
  3. 界面上用网格实时展示缺失帧的序号(0 = header)
  4. 发送端可以通过"Show frame"功能单独显示某一帧,供接收端请求补发缺失块
  5. 所有帧收齐后,计算 CRC 与 header 中的值比对,一致则提供下载按钮

整个接收逻辑没有复杂的滑动窗口或重传协商——发端循环播放,收端按序号自己拼图。 这在协议层面极度简化,但也意味着传输效率受限于相机帧率和 QR 码密度。

分块策略与性能约束

README 给出了一些实测参考值:

参数典型值说明
每帧字符数500受 QR 码密度限制
传输速率~1.1 KB/s(Base64)/~0.83 KB/s(原始)取决于帧率和编码量
100 KB 文件约 2 分钟完成一轮循环接收端通常需要 1-2 轮

影响传输速度的三个变量:

  • Chunk size(块大小):越大越快,但 QR 码密度越高,老设备解码失败率上升
  • FPS(帧率):越高越快,但接收端相机帧率不够时会漏帧
  • ECC(纠错级别):越高越抗损,但同样会增大 QR 码密度

速度、可靠性、兼容性三者不能同时最优。 README 建议在老设备上降低 FPS、升高 ECC、缩小 chunk 到约 300 字符,这是针对兼容性的保守配置。

适用边界

ShadowCat 适合以下场景:

  • 老旧手机之间需要传递文件,但没有网络或蓝牙可用
  • 在网络受限环境下两台设备需要交换小文件(纯离线)
  • 作为教学案例,理解"分块 + 确认 + 重传"的朴素传输模型

不适合以下场景:

  • 需要传输大文件(MB 级别):QR 码传输速度太慢,不现实
  • 追求高可靠性传输:高纠错 QR 码密度极高,相机对焦稍有问题就大量丢帧
  • 网络可用时:直接用 localtunnel、Python http.server 或 anywire 等工具都远比 QR 码高效

阅读路径

如果想深入理解或改造 ShadowCat,建议按以下顺序阅读源码:

  1. qrcode.html 中 Encode/Decode 函数,理解 Base64 分块逻辑
  2. Protocol 注释,理解帧格式定义
  3. 接收端缺失块追踪器的状态管理逻辑
  4. 结合 README 的 Practical notes,调整分块参数验证不同设备上的表现

总结

ShadowCat 是一个用朴素工程思维解决的问题:没有网络、没有蓝牙,只有屏幕和摄像头。那就用屏幕发,用摄像头收。协议设计值得注意的地方不在"多巧妙",在"多克制"——只有两种帧格式、解析只要 split('|')、接收端逻辑就是一张序号集合。简单到可以直接在浏览器控制台里复现核心逻辑,这对于学习和调试来说本身就是价值。