中文 | English
基于 sz3/libcimbar 的大文件光学传输工具——通过多 fountain stream 并行突破单 stream 容量上限(libcimbar Mode B 单 stream wirehair cap ~40.5 MB),无网络环境下实测传输 100+ MB 文件(理论上限 ~1.2 GB / 单会话, 详见 manifest-spec)。
cimbar-bigfile 在 libcimbar 之上做了一层纯 HTML/JS 包装,把大文件切成 chunk(默认 10 MB,libcimbar 作者推荐 10-15 MB sweet spot,留出冗余 headroom),每块独立用 fountain code 编码(不同 encode_id),在屏幕上依次播放彩色码动画。
接收端用作者的 CameraFileCopy (CFC) 安卓应用扫码,CFC 内置的 fountain_decoder_sink 已经原生支持按 encode_id 并发分桶解码,完全无需修改。所有块收完后用浏览器打开拼接页面,拖入文件即可还原原始大文件。
[发送端 send.html] → 屏幕动画 → [手机 CFC] → 保存 N 个块 → [拼接 reassemble.html] → 原文件
- 接收端:手机安装 CameraFileCopy (F-Droid / Google Play / GitHub Release APK)
- 发送端:电脑浏览器双击打开
send.standalone.html(自包含单文件版,无需联网,推荐) - 拼接端:任意浏览器双击打开
reassemble.html(零依赖纯 JS,无需联网)
也可以用模块化的
send.html(开发版),但需要先起本地 HTTP server(见下文 开发 段),因为浏览器在file://协议下会拦截 wasm 文件加载。普通用户用 standalone 版更简单。
发送页和拼接页的界面默认使用英文。点击页面右上角的 中文 / EN 按钮可以随时切换语言;浏览器会记住选择,刷新或下次打开页面时继续使用所选语言。
- 打开
send.standalone.html - 把要传的文件拖入页面
- 点击 "Start"(中文界面为“开始传输”),屏幕开始播放彩色码动画
- 保持屏幕不动直到所有块发完
也可以在发送端先临时收集内容再发送:
Ctrl+V粘贴文本:页面会自动生成一个clipboard-*.txt暂存项Ctrl+V粘贴剪贴板里的文件,或点击 "添加文件" / "添加文件夹":加入暂存列表- 暂存区只有一个普通文件或一段粘贴文本时,按钮显示 "开始传输",直接发送原文件
- 暂存区有多个文件,或内容来自 "添加文件夹"(即使文件夹中只有一个文件)时,按钮显示 "打包并开始传输",浏览器本地生成
cimbar-bundle-*.tar后发送
需要打包时,过程全在浏览器本地完成,不上传网络;接收结果是无压缩的 cimbar-bundle-*.tar,必须再用 Windows 自带 tar、7-Zip 等工具解包。从 "添加文件夹" 选择的内容即使只有一个文件,也遵循这一规则。
大文件(多块)模式下,发送端 UI 自动切到「三栏响应式布局」:左栏 = 跳转列表,中间 = cimbar 码 canvas,右栏 = ✅ 已保存按钮 + 进度面板。
默认行为是发送方按 manifest → part00 → part01 → ... → 循环回 manifest 的顺序循环播放,CFC 扫到哪块靠随机命中——100MB 文件总扫描时间约 ~55 分钟。
3 种操作消除等待:
| 操作 | 效果 | 触发 |
|---|---|---|
| 单击块按钮 | 立即跳转到该块(清锁,自由循环) | 鼠标单击 / Tab + Enter |
| 双击块按钮 | 🔒 锁定该块(发送方专攻该块, fountain 持续补帧, CFC 下次扫到必然是它);再次双击 / 切别块 = 转移 / 解锁 | 鼠标双击 / Tab + Shift+Enter(键盘等效) |
| ✅ 已保存, 跳下一块(右栏绿色主按钮) | 当前块标 ✓ 已保存(持久化到 localStorage)+ 自动跳到下一个未完成块 | CFC 弹"保存到哪里"对话框且保存完后点 |
按钮状态视觉(截图左栏):
- 🟢
✓ 前缀 + 绿底= 已确认保存 - 🔵
蓝底= 屏幕正在播放(active) - 🟡
淡琥珀 + 🔒= 双击锁定的块;进度面板同步显示 "🔒 锁定 partXX · 补帧 N 轮"
预期收益:100MB 文件总扫描时间从 ~55 分钟降到 ~17 分钟(消除随机扫描等待)。
simpleMode(小文件 ≤ 块大小,单文件直发)下隐藏跳转列表——只有 1 个 stream,跳转无意义。
传输开始后,点击发送页的 "Fullscreen code"(中文界面为“全屏显示码图”)可将 cimbar 码图放大到全屏,方便手机相机对焦和扫描。全屏模式隐藏左右操作栏,但会在码图外保留状态栏。大文件会进入 guided lock:锁定当前(或下一个未完成)分片,持续补帧,并显示当前分片、总分片数、已保存数量以及“✅ 已保存,锁定下一块”按钮;状态栏会常驻,避免手机保存期间发送端切到别的分片后误标。
- 按
Esc或点击浮动退出按钮可返回普通布局 - CFC 保存完当前分片后,直接在全屏状态栏确认;发送端会切换并锁定下一个未完成分片
- 全部分片标记完成后,状态栏会提示可以停止传输并禁用继续确认
- guided lock 状态栏常驻;移动鼠标或用键盘聚焦时,自动淡出的退出按钮会重新出现
- 退出全屏会解除本次全屏自动施加的锁;进入全屏前已有的手动锁会保留
- 点击停止传输时会自动退出全屏
- 手机打开 CFC,对着电脑屏幕
- 每完成一块,CFC 会弹出"保存到哪里"对话框
- 每次都选同一个目录(推荐建一个
cimbar-bigfile-job1/目录) - 全部接收完后:
- 小文件(≤ 块大小,默认 ≤ 10 MB):直接 1 个原文件名的文件,不需要拼接,CFC 落盘的就是原文件
- 大文件(> 块大小):N+1 个文件
manifest.json+<filename>.part00.bin...<filename>.partNN.bin,需要走下面的拼接步骤
如果发送的是
cimbar-bundle-*.tar,无论它作为小文件直接接收,还是作为大文件先完成拼接,最终都需要再解包。
- 把手机里的所有文件传到电脑(USB / 邮件 / 任何方式)
- 浏览器打开
reassemble.html - 全选所有文件拖入页面
- 自动校验 SHA256 → 通过 → 自动下载还原后的原文件
拼接页同样默认使用英文,可通过右上角的 中文 / EN 按钮切换语言;切换不会清空已选择的文件或当前校验状态。
- 每块完成 CFC 弹一次保存对话框:10MB / 块时,100MB 文件需要点 11 次"保存"。
- 吞吐量:约 106 KB/s(libcimbar Mode B 默认)。100MB 文件预计耗时 16-20 分钟。
- 接收端推荐 Android CFC:libcimbar 上游 v0.6.4 release 也带 web 端解码器(即本仓库
vendor/cimbar-wasm-v0.6.4/recv.html),但实测大文件(≥ 3MB)会中途停滞(见 issue #4),且它是单 stream 设计,无法直接消费本仓库的多 stream 分块输出(不支持按encode_id分桶解码)。
| 症状 | 可能原因 | 解决方法 |
|---|---|---|
| CFC 扫不到任何码 | 屏幕亮度低 / 距离不对 | 屏幕调最高亮度,离手机 10-30 cm |
| CFC 扫描很慢 | 帧率太高摄像头跟不上 | send.html 把 FPS 调到 10-12 |
| 某些块没收到 | fountain 冗余不够 | send.html 把 "冗余" 调到 2.0 或更高,让发送端给每块多发些帧 |
| 拼接 SHA256 校验失败 | 某块在传输中损坏 | 看哪块失败 → 重新发送(用同一 encode_id_base 重启 send.html,跳到那块) |
| 浏览器加载 wasm 失败 | file:// 协议被 CORS 拦截(开发版 send.html 才有此问题) |
改用 send.standalone.html(双击即可),或用本地 HTTP server:python -m http.server 8000 访问 http://localhost:8000/send.html |
| 文件名乱码 | 系统字符编码问题 | manifest 强制 UTF-8,检查浏览器/手机系统编码 |
| 浏览器卡顿 | 文件太大 wasm 堆压力 | 降低单块大小(默认 10MB → 5MB) |
| 总扫描时间太长 | CFC 每次保存重置 fountain 状态,发送方循环导致命中靠运气 | 用新加的「跳转按钮」主动指定下一个目标 chunk(见上方"加速接收"段) |
参考环境:1080p 屏幕 + Pixel 5 + 默认参数(Mode B / 15 fps / 10 MB 块 / 2.0x 冗余)
| 文件大小 | 块数 | 弹窗次数 | 预估耗时 | 参考吞吐 (Mode B) |
|---|---|---|---|---|
| 5 MB | 1 块(直发,无 manifest) | 1 次 | ~1 分钟 | ~85 KB/s |
| 28 MB | 3 块 | 4 次(manifest + 3 块) | ~4-5 分钟 | ~100 KB/s |
| 100 MB | 10 块 | 11 次(manifest + 10 块) | ~16-20 分钟 | ~106 KB/s |
验证覆盖:5 MB(bundled
test/test-5m.bin)+ ~28 MB(手动光学链路 end-to-end)+ 100 MB(应用层 round-trip viascripts/test-round-trip-100mb.js)。其他规模线性外推。实际吞吐受光线、屏幕亮度、相机自动对焦稳定性影响很大。
libcimbar 作者在 sz3/libcimbar#165 评论 中明确 "no penalty for redundant blocks (e.g. 3x or 4x for 10 MB chunks)"——也就是说 fountain code 的本质决定了:
- 冗余只影响发送端的发帧总数,不影响 CFC 接收完成所需帧数(CFC 只要凑齐足够独立帧就完成)
- 多余的帧 CFC 会被自动忽略,不浪费接收时间
- 高冗余的实际价值是:单 chunk 第一轮没收齐时,发送端循环里继续补帧的兜底厚度
| redundancy | 适用场景 |
|---|---|
| 1.2-1.5x | 屏幕固定支架 + 良好光线 + 对焦稳定(aggressive 预设) |
| 2.0x(默认) | 一般室内光线 + 手持稳定(balanced 预设) |
| 3.0-4.0x | 差光线 / 反光 / 手抖明显(conservative 预设) |
发送端 UI 的 redundancy 输入框上限放宽到 5.0x,覆盖极端场景。
单次会话受 wirehair uint16_t encode_id slot 限制,chunk_count 应保守约束在 ~120 块以内(即 ~1.2 GB at 10MB/chunk)。libcimbar 作者在 sz3/libcimbar#165 评论 提到两种突破方法,但都需要用户手动 babysit:
方法 1:变 chunk size 重启会话
"Provided you're finished sending a chunk, you can re-use the encode_id if you slightly vary the chunk size... (e.g. 10.01 MB chunks after the first go around)"
第一轮把文件前 ~1.2 GB 用 10 MB chunk 发完;第二轮把剩余部分用稍微不同的 chunk size(如 10.01 MB 或 10.5 MB)重启发送端。wirehair 会把不同 chunk size 视为新文件,不会占用旧的 encode_id slot。
方法 2:CFC 端重启 decoder 清缓存
"you can also restart the decoder to clear its cache of 'done' files, which will have the same effect without changing the chunk size"
把 CFC 应用整个重启一次,它的 fountain_decoder_sink 内部 "done files" 缓存被清空。之后同 encode_id 可以复用。但前一轮的部分进度也会一并丢失——只适合"前一轮已全部完成保存"后的新一轮场景。
详见 vendor/README.md 的 "Updating to newer libcimbar release" 章节。
- docs/manifest-spec.md - manifest JSON 字段语义
- docs/architecture.md - 数据流、wasm API、设计决策
# 起本地 HTTP 服务器(避免 file:// CORS 问题)
python -m http.server 8000
# 浏览器访问 http://localhost:8000/send.htmlsend.standalone.html 是给最终用户的"双击即用"版本,把 vendor wasm + glue js 都 base64 inline 进 HTML,脱离 HTTP server。每次升级 vendor 后必须重新构建:
PYTHONIOENCODING=utf-8 PYTHONUTF8=1 python scripts/build-standalone.py
# 输出 send.standalone.html (约 2.5 MB)构建脚本只读 send.html + vendor/cimbar-wasm-v0.6.4/cimbar_js.*.js + cimbar_js.*.wasm,输出独立文件到仓库根目录。原理:替换 <script src="vendor/..."> 为 inline <script> 把 base64 解码成 Module.wasmBinary,emscripten glue 检测到就跳过 fetch。
cimbar-bigfile 是 libcimbar 之上的轻量包装器。本仓库由 MIT、MPL-2.0、BSD-3-Clause 三个许可证共同管理:
| 部分 | 许可证 | 版权 | 说明 |
|---|---|---|---|
send.html / reassemble.html / docs/* 等本项目原创代码 |
MIT | © 2026 peipei | 详见 LICENSE |
vendor/cimbar_js.html / vendor/cimbar-wasm-v0.6.4/* |
MPL-2.0 | © sz3 (libcimbar) | 详见 vendor/LICENSE-libcimbar |
| 上述 wasm 内嵌的 wirehair fountain code 库 | BSD-3-Clause | © 2018 Christopher A. Taylor | 详见 vendor/LICENSE-wirehair |
| 用户独立安装的 CFC Android 接收端 (未 vendor) | MIT | © sz3 | 见 github.com/sz3/cfc |
MPL-2.0 source obligation:本项目分发了 libcimbar 的 wasm 二进制(Executable Form),按 MPL-2.0 §3.2,对应的 source code 可在 https://github.com/sz3/libcimbar/tree/v0.6.4 免费获取。
- sz3/libcimbar — 核心 fountain code + cimbar 编解码引擎
- sz3/cfc — 接收端 Android 应用
- catid/wirehair — 底层 fountain code 库
