14 KiB
本地备份同步七牛
独立 Node.js 脚本项目,使用官方 qiniu SDK 7.15.2。入口命令在 doodle-store 根目录执行。只复制本地文件到已存在的七牛空间,不创建空间、不删除/覆盖远端对象、不修改数据库或业务 URL。
前置步骤:通过 tcb 下载云存储备份
更新日期:2026-10-20。本节记录日后重新导出资源的操作流程;本次编写文档未登录云环境或执行下载。已有备份位于 /Users/joey-xd/sites/doodle/doodle-assets/cloud_backup,无需为了使用同步工具重复下载。
流程为:原小程序云存储 → tcb 下载到本地 → SOURCE_DIR 指向备份根目录 → asset-sync 上传七牛 → 按需回填数据库。这里下载的是云存储文件,不是数据库记录、云函数代码或完整环境备份。
当前线上版小程序仍使用旧云开发,原预拉取模式、地址、生成链路和全部旧云函数必须保留。本流程只读取源文件,不删除源文件、不调整存储权限、不停用云函数或定时任务;后续的七牛同步也不会替代旧线上服务。
1. 安装并检查 CloudBase CLI
本机已安装 @cloudbase/cli,本次通过版本及帮助命令核对的版本为 3.8.7。新机器可安装同一版本复现操作;已有 tcb 时先检查版本,不必重复安装:
# 仅新机器未安装时执行
pnpm add -g @cloudbase/cli@3.8.7
tcb --version
tcb storage download --help
使用当前的空格形式 tcb storage download;旧文档可能使用冒号形式 tcb storage:download。升级 CLI 后先重新查看帮助。参考 官方安装说明。
2. 登录并确认原云环境
以下 tcb 命令在 doodle-mini 目录执行,七牛同步命令则在 doodle-store 根目录执行:
cd /Users/joey-xd/sites/doodle/doodle-mini
# 按终端提示在浏览器中完成授权
tcb login
# 核对账号能访问的环境
tcb env list
# 显式指定原小程序环境,避免误用其他项目的默认环境
tcb storage list -e cloud1-9gifs7a2756e2c87
环境 ID 来自 doodle-mini/cloudbaserc.json。如果更换源环境,应重新核对并替换下列命令中的 ID;不要直接复制 cloud:// 文件 ID 作为环境 ID。
授权账号必须能管理原小程序云环境的云存储。微信 AppSecret 和七牛 AK/SK 均不是 tcb 登录凭据。若列表中看不到原环境,先核对账号权限及环境所在地域;必要时按实际地域使用全局 --region 参数,不为下载另建环境或改变权限。
3. 下载全部资源
每次新备份使用独立目录,保留已有 cloud_backup。在同一个终端依次执行:
DOODLE_CLOUD_BACKUP="/Users/joey-xd/sites/doodle/doodle-assets/cloud_backup-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$DOODLE_CLOUD_BACKUP"
tcb storage download / "$DOODLE_CLOUD_BACKUP" --dir -e cloud1-9gifs7a2756e2c87
/ 表示下载云存储全部文件,--dir 表示目录下载。云端路径使用相对对象路径,不使用临时 HTTPS 链接。命令依据 官方云存储下载说明。本项目源端为旧小程序云环境,不因新后端采用 PostgreSQL 就改用 storage objects 等 PG 环境命令。
如果只补下载题型预览,可改用目录下载,目标仍保留 assets/previews 层级;这是全量下载的替代方式,无需两者都执行:
mkdir -p "$DOODLE_CLOUD_BACKUP/assets/previews"
tcb storage download assets/previews "$DOODLE_CLOUD_BACKUP/assets/previews" --dir -e cloud1-9gifs7a2756e2c87
其他目录按实际云端清单选择,例如 assets/fonts、assets/materials、content、user-avatars。云端仍在写入,目录下载不是某个时间点的原子快照;下载失败或重试后都要核对实际文件,不把命令退出成功等同于完整备份,也不将旧目录混入新备份来掩盖缺失。
4. 核对目录并连接七牛同步
备份根目录应保留云端相对路径,例如:
cloud_backup-<时间>/
├── assets/
│ ├── fonts/
│ ├── materials/
│ └── previews/
│ ├── chinese/
│ ├── english/
│ ├── math/
│ ├── papers/
│ ├── pinyin/
│ └── puzzle/
├── content/
│ └── page-config.json
└── user-avatars/
这只是路径示例,不代表每个目录必有文件;以本次云端列表和下载结果为准。不要多套一层环境 ID、重复的 previews 目录,或直接把文件平铺到备份根目录,否则七牛对象 key 会变化。
抽查图片能打开、字体文件非空、content/page-config.json 能解析,并对照云端列表核对数量和大小。历史备份为 62 个文件,其中预览 51 个、头像 7 个、字体/素材/首页配置/demo 各 1 个;这是旧备份基线,不是未来下载必须满足的固定数量。demo 等其他对象也应保留原路径,不限于上方示例目录。
检查完成后,将 packages/asset-sync/.env 的 SOURCE_DIR 手动改为新备份的实际绝对路径;继续使用已有备份时保持原值:
SOURCE_DIR=/Users/joey-xd/sites/doodle/doodle-assets/cloud_backup
不要把包含 $DOODLE_CLOUD_BACKUP 的字符串直接写进 .env;该配置需要实际路径。SOURCE_DIR 指向包含 assets、content、user-avatars 的根目录,不是 assets/previews 子目录。备份目录不应存放下载日志、清单或报告,这些普通文件也可能被同步工具扫描上传。
回到 doodle-store,先预演并核对报告中的相对路径、目标 key 和文件数量,再正式上传:
cd /Users/joey-xd/sites/doodle/doodle-store
pnpm assets:sync
pnpm assets:sync --apply
本项目当前使用 QINIU_KEY_PREFIX=doodle-assets/,补同步已有资源时应保持该值,并与 server 配置一致;下方配置段中的 doodle/ 是工具默认示例,不能在补同步时误换前缀。同步与数据库回填是独立步骤,详见本文末尾。
配置
已生成本机 packages/asset-sync/.env,源目录配置为:
SOURCE_DIR=/Users/joey-xd/sites/doodle/doodle-assets/cloud_backup
QINIU_AK=
QINIU_SK=
QINIU_BUCKET=
QINIU_REGION=
QINIU_KEY_PREFIX=doodle/
QINIU_PUBLIC_DOMAIN=
SYNC_CONCURRENCY=3
SYNC_RETRIES=2
SYNC_MAX_FILE_BYTES=67108864
填写本地 .env 即可,不要将 AK/SK 写到小程序、Git、命令参数或聊天中。该文件被 Git 忽略,并以 0600 权限保存。新机器可从 .env.example 复制;相对 SOURCE_DIR 以配置文件目录为基准,运行目录不影响源路径。进程环境变量优先于文件配置。
| 配置 | 说明 |
|---|---|
| SOURCE_DIR | 本地备份根目录,工具只读 |
| QINIU_AK / QINIU_SK | 七牛服务端 AccessKey / SecretKey,仅正式同步要求 |
| QINIU_BUCKET | 所有资源共用的公开空间:图片、头像、字体、JSON 等 |
| QINIU_REGION | 可留空自动查询;固定区域时使用七牛 region ID,如 z0、z1、z2、na0、as0 |
| QINIU_KEY_PREFIX | 对象前缀;默认 doodle/,空串为原相对路径 |
| QINIU_PUBLIC_DOMAIN | 可选 HTTPS origin,例如 https://cdn.example.com;不含 /doodle 等路径,仅用于生成报告中的公共 URL,不证明域名已经绑定该空间 |
| SYNC_CONCURRENCY | 并发文件数,1~10,默认 3 |
| SYNC_RETRIES | 瞬时错误额外重试次数,0~5,默认 2 |
| SYNC_MAX_FILE_BYTES | 每个文件读取内存的大小限制,默认 64 MiB |
空间必须已存在且有权查询和上传。执行前读取空间设置,核对目标空间实际 private=0,工具不会自动调整 ACL。头像与其他资源使用同一个 QINIU_BUCKET,无需配置私有空间。
使用
# 查看用法
pnpm assets:sync --help
# 默认预演全部文件,不调用七牛接口,也不要求 AK/SK
pnpm assets:sync
# 只预演题型预览图
pnpm assets:sync --only assets/previews
# 填写 .env 后,先同步 51 张预览
pnpm assets:sync --apply --only assets/previews
# 可选择多个目录
pnpm assets:sync --apply --only assets/fonts --only assets/materials
# 全部同步,含头像,均使用 QINIU_BUCKET 公开空间
pnpm assets:sync --apply
# 自定义配置文件/报告;报告文件不能已存在,也不能位于源备份内
pnpm assets:sync --env /绝对路径/.env.sync --report /绝对路径/report.json
--only 按目录前缀或精确文件路径筛选,不是 glob;--dry-run 可显式声明预演,与 --apply 互斥。通过默认预演确认清单后,再添加 --apply 上传。文件缺失、损坏 JSON、超限文件、软链接及无法识别的预览/头像图片会停止预检。
对象路径和同步行为
默认映射:
assets/previews/math/number-find.jpg
→ 公共 bucket : doodle/assets/previews/math/number-find.jpg
user-avatars/<文件名>.png
→ 同一公共 bucket : doodle/user-avatars/<文件名>.png
头像保留 user-avatars/ 相对路径,与预览等资源共用公开空间。配置 QINIU_PUBLIC_DOMAIN 后,报告也会生成头像的公开地址。工具不根据文件名推断用户身份,用户归属仍需在后续绑定时核实。
- 扫描源目录,忽略隐藏文件,记录实际 MIME、字节数、SHA-256 与七牛 ETag;不修改源文件。
- 正式执行时重新读取并核对本地摘要,使用该次读取的 Buffer 上传,避免核对后源文件被修改。
- 远端不存在时,用指定 bucket:key 的 insertOnly=1 凭证上传,仅允许新增,启用 SDK CRC 校验和 HTTPS。
- 远端同 key 且 ETag/大小一致:跳过并标记已核对。不同内容:报告 conflict,保留原远端文件;需要新版本时选择新的前缀/key。
- 上传完成再次 stat,对比远端 ETag/大小;不是仅凭“请求成功”就记完成。
- 瞬时网络错误、429、5xx 有限重试,鉴权错误和冲突不盲目重试。上传已成功但响应丢失时,重试遇到 614 后回查摘要恢复。
- 任一文件失败或冲突时命令退出码为 1,其余文件结果仍写入报告。中断后重新运行同一命令,会重新检查远端并跳过已完成对象;未提供分片级断点续传。
校验方式是七牛内容 ETag 和大小,不等同于从 CDN 下载全部文件后比较 SHA-256。SHA-256 是本地备份摘要,报告明确记录 verifiedBy=qiniu-etag-and-size。不会自动刷新 CDN 或替换同名对象。
报告与后续接入
默认报告写 packages/asset-sync/reports/<时间>-<随机ID>.json,目录被 Git 和格式化工具忽略,文件权限 0600;含源相对路径、目标 bucket/key、MIME、大小、摘要、状态和有限错误码。不包含 AK、SK、上传凭证、签名链接或 SDK 原始异常。中断时报告可能未写完,以重新运行后的远端校验为准。
主要状态:planned(预演)、uploaded(上传并核对)、skipped(已有同内容)、conflict(同名异内容)、failed、not_attempted(全批配置/权限预检未通过)。常见全批错误:CREDENTIALS_REQUIRED、BUCKET_REQUIRED、BUCKET_VISIBILITY_MISMATCH。上传用户需有 bucketInfo、stat、upload 所需权限。
这次只做文件同步。同步成功后仍需按报告映射更新新后端 assets(保持现有 ID 并回填 bucket/key/MIME/摘要、status=ready)、字体配置、用户头像绑定及首页 JSON 内的 cloud 引用。content/page-config.json 当前会原样复制,不会自动转换嵌套链接;原云上传和定时任务也不会自动切换到七牛。
验证与当前预演
pnpm assets:test
pnpm check
测试使用临时本地文件和模拟七牛接口,覆盖配置、ETag 边界、头像同空间映射、预演无网络、首次上传、重复跳过、冲突保护、重试、响应丢失恢复、源文件变化、校验失败、凭据脱敏及 CLI 报告权限。没有使用真实 AK/SK 或执行真实上传。
本地备份预演:62 个文件,共 3,568,778 字节;其中题型预览 51、头像 7、字体 1、素材 1、首页配置 1、demo 1。该数量为本地预演结果,不代表已上传。
实现依据:七牛官方 Node.js SDK、七牛上传策略。
回填服务端资产
上传报告确认成功后,根目录执行 pnpm assets:map --report /绝对路径/报告.json 预演,再添加 --apply 写入本机 doodle_dev。该独立回填步骤会重新核对七牛对象与备份摘要,事务更新已有题型预览资产,保留资源 ID 并记录审计;不处理头像归属、首页 JSON 嵌套链接或其他资源绑定。配置必须与 server 的域名、前缀及空间一致,详见根 README。