Files
doodle-store/docs/v3.5/云存储图片迁移七牛调研.md

106 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 小程序云存储图片迁移七牛调研
调研日期:2026-10-16。范围:官方能力核实及本项目实施方案;未执行云端文件下载、上传、权限调整或删除。
## 结论
可以迁移。针对已经导入的 51 个题型预览,建议采用 **原云开发授权下载 → 本地备份与校验 → 七牛上传 → 回读校验 → 更新现有 assets 为 ready**。这是一套文件迁移任务,保持题型 ID、preview_asset_id 及原计数/业务时间不变,不必重新导入题型。
七牛直接抓取 HTTPS URL 也可作为备选,但需要先解决链接授权、失效与源站访问限制。当前只有 51 张预览,优先选择便于留存原文件、校验和重试的本地中转方式。这一优先级是结合项目规模作出的工程判断。
## 官方能力核实
| 环节 | 已核实能力 | 对本项目的意义 |
| -------------- | ------------------------------------------- | -------------------------------------------- |
| 云开发文件获取 | SDK 可按 fileID 下载,或换取 HTTPS 下载链接 | 使用现有 source_ref,不从文件名猜下载地址 |
| 云开发目录备份 | CLI 支持下载文件和目录 | 可先备份 assets/previews 目录 |
| 七牛上传 | Node.js SDK 支持服务端上传及 stat 查询 | 能集成现有 Node.js/Prisma 工程并自动回填映射 |
| 七牛抓取 | fetch 可抓取指定 URL 到指定 bucket/key | 可减少本地转发,但不直接识别 cloud:// |
上述能力依据 [CloudBase 文件管理](https://docs.cloudbase.net/storage/sdk)、[CloudBase CLI 云存储](https://docs.cloudbase.net/cli-v1/storage)、[七牛 Node.js SDK](https://developer.qiniu.com/kodo/sdk/nodejs) 和 [七牛第三方资源抓取](https://developer.qiniu.com/kodo/api/fetch)。
## 获取源图片的两条路径
### 路径 A:CLI / 管理 SDK 下载原文件
CloudBase 官方 CLI 提供 `tcb storage download <cloudPath> <localPath>`,下载目录使用 `--dir`。以下是选择并确认原云开发环境后的目录下载形式,仅供实施时参考,尚未在本项目账号执行:
```bash
# 先确认安装版本支持的参数,并用有权限的账号登录、明确原环境
tcb storage download --help
# 在明确原环境的配置/参数下,备份预览目录
tcb storage download assets/previews ./cloud-backup/previews --dir
```
目录下载可能包括不在当前 51 条题型中的旧文件;上传和数据库绑定仍按这 51 条资产清单选择。若要完整备份,后续另行盘点头像、字体和页面配置等目录,不将未引用文件直接混入本批次。[官方目录下载说明](https://docs.cloudbase.net/cli-v1/storage)
自动化也可选 `@cloudbase/manager-node` 的文件/目录下载能力。官方管理 SDK 使用 Tencent Cloud SecretId/SecretKey 和 envId 初始化;实际必须确认账号能够管理这个小程序原环境,不能把已有 WECHAT_MINI_APP_SECRET 当作腾讯云管理密钥。原小程序云环境与目标本地 PostgreSQL 是两套系统,目标选用 PostgreSQL 不意味着源端要改用 CloudBase 的 PG 存储接口。[管理 SDK 存储文档](https://docs.cloudbase.net/api-reference/manager/node/storage)
官方管理 SDK 还说明目录不存在时可能不下载且不抛错,因此必须按清单核对实际下载的 51 个文件,不能只看命令退出成功。SDK 版本的参数形式存在差异,实施时以锁定版本为准。[目录下载说明](https://docs.cloudbase.net/api-reference/manager/node/storage)
如果账号看不到原环境,先核实绑定和管理权限,不创建替代环境或调整原环境为公开读来绕过授权。具体账号关系仍需实际控制台确认。[CloudBase 账号与环境关联](https://docs.cloudbase.net/faq/account)
### 路径 B:原环境兑换下载链接
在已授权的原云开发运行环境中,使用相应 SDK 的 getTempFileURL,再由本地任务下载。官方存储文档说明每批最多 50 个链接,因此本批 51 个 fileID 可分成 50 + 1,逐项检查成功状态;授权链接在使用前获取,过期重新兑换。[官方临时链接说明](https://docs.cloudbase.net/storage/sdk)
不要把所有 HTTPS 地址都认作临时地址:公有读文件可获得稳定链接,私有读文件通常返回有时限的授权链接,有效期随接口和配置而异。带 sign/t 等授权参数的地址应按临时访问凭证处理,不写入长期资产字段,不放入迁移报告或日志。资产身份继续使用原 fileID/source_ref_hash,而非某次生成的 URL。[官方权限与链接说明](https://docs.cloudbase.net/storage/sdk)
若需要临时管理云函数,只允许管理员/受控任务调用,限定原环境及本次文件清单,完成后撤除;本次不部署该函数。wx.cloud、wx-server-sdk、CloudBase 管理 SDK 的运行位置和凭据不同,实施时明确选择一种,先验证 1 张原图,不能照搬另一 SDK 的认证示例。
## 迁入七牛的选择
### 推荐:备份后由 Node.js 上传
利用七牛服务端 SDK 上传本地文件,并用资源查询取得目标信息;大批量纯文件同步也可用官方 qshell 的 qupload,但本项目还需自动维护资产和数据库审计,选择与现有工程集成的任务更合适。[七牛 SDK](https://developer.qiniu.com/kodo/sdk/nodejs)、[qshell 工具](https://developer.qiniu.com/kodo/1302/qshell)
建议实施流程:
1. 从本地 assets 筛选 purpose=preview、status=pending,导出 assetId → source_ref → 本地备份路径清单,记录原环境和源导出摘要。
2. 下载原文件,检查返回的是可解码图片,记录实际 MIME、字节数和 SHA-256;不把错误 HTML 页面或仅改扩展名的内容当作图片。
3. 将对象上传到开发环境专属空间/路径。对象 key 使用资产 ID 和内容版本,例如 `dev/previews/<assetId>/<sha256>.jpg`;扩展名以实际文件类型为准。这是本项目命名建议。
4. 核对返回 key、大小和内容。不同算法的 hash 不直接比较;本批只有 51 张,可全部从七牛原文件回读并计算同一 SHA-256,与备份逐一比对。
5. 文件操作全部完成后,每个资产使用短事务锁行并复核原 source_ref/status,再写 bucket、object_key、object_ref_hash、mime_type、size_bytes、checksum、status=ready 和迁移审计。
6. 保留资产 ID、原 source_ref/source_ref_hash 和题型 preview_asset_id,题型原始 createdAt/updatedAt、downloads/likes/seed 不因换存储而重写。
7. ready 资产重新执行时核对目标后跳过;失败记录可重试状态及原因,保留原备份。同名异内容不能直接覆盖。上传成功但数据库提交失败时,按既定 key 查询和校验,避免重试生成另一对象。
七牛资源查询能返回目标 hash、fsize、mimeType 等信息;完整内容回读、SHA-256 和事务顺序是本项目提出的验收设计,不是已完成的迁移结果。[七牛 SDK 资源查询](https://developer.qiniu.com/kodo/sdk/nodejs)
### 备选:七牛 fetch / batchfetch
流程为 `fileID → 新鲜有效的 HTTPS 下载链接 → 七牛 fetch → 校验 → 回填资产`。七牛官方 fetch 每次处理一个文件,可指定目标 key;受源站 IP、UA 等访问限制影响。批量可用 qshell batchfetch,仍需逐项核实结果,不能仅按任务提交成功把资产改成 ready。[fetch API](https://developer.qiniu.com/kodo/api/fetch)、[qshell](https://developer.qiniu.com/kodo/1302/qshell)
该路线适合已有可靠链接清单、文件较多的后续场景。签名过期就重新取得链接,不把旧签名清单作为长期重试数据。抓取后也要验证大小/类型/内容,403、404 及失败行单独记录。是否能从七牛网络访问当前源站,需要用本账号实际测试,官方能力支持不代表该环境已经验证成功。
七牛还提供 URL/第三方存储迁移工具。直接对接 S3/COS 需要真实源存储权限和相应端点;仅凭 cloud:// fileID 或小程序 AppSecret,不足以推定可以使用底层存储迁移通道。[七牛文件迁移工具](https://developer.qiniu.com/kodo/6284/file-backup-migration?portal_modal=1)
## 预览和头像存储与绑定
| 资源 | 本项目建议 | 绑定方式 |
| ------------------ | ------------------------------------------ | ------------------------------ |
| 题型预览、公共字体 | 公共资源空间,业务 API 生成稳定 HTTPS 地址 | 保留现有预览资产 ID |
| 用户头像 | 与预览共用公开空间,使用稳定 HTTPS 地址 | 迁移用户后按原 userId 核实归属 |
七牛私有空间下载需要授权 URL,公开空间可使用域名与对象 key 生成地址;生产资源域名应使用已配置的自定义域名,避免依赖测试域名。[七牛下载方式](https://developer.qiniu.com/kodo/kb/1321/how-to-acquire-the-outside-storage-file-links)
当前按用户确认,头像与预览等资源统一使用 QINIU_BUCKET 公开空间;开发/生产隔离方案仍需在接入时确定。仅在文件名里加入 private 或 avatars,并不等于完成访问控制。头像必须依据 users 导出中实际引用和身份关联迁移,不从文件名猜当前用户头像,也不批量绑定已经失联/注销用户的文件。
## 实施前需要的配置
- 源端:原 EnvId,以及能读取该环境文件的 CLI 登录/管理权限,或原环境受控 SDK 运行权限。
- 七牛:AccessKey/SecretKey、目标 Bucket、区域,以及配置 HTTPS 的资源域名;当前头像与其他资源共用公开空间。
- 本地:受控备份目录、文件清单和迁移报告目录。密钥仅放受控环境,不写进文档、命令历史或普通日志。
建议第一批只执行 51 张预览:先验证 1 张,再迁移全部;验收 `51 个引用 = 成功校验数 + 明确失败数`,全部成功后为 51 个 ready。失败不应混成成功,不改题型计数或审核外的年级数据。
后续再盘点头像、字体、分类文件图标、首页 JSON 嵌套图片、分享图及小程序硬编码/缓存中的 cloud://。旧版本仍可能使用旧引用,完成新版切换和兼容期核对前保留源文件。新页面从 API 获取七牛地址,按实际使用的微信网络接口配置合法域名,并通过真机验证。
当前建议的下一步是:确认原云开发下载权限与七牛开发空间配置,然后实现“下载备份、上传校验、资产回填”脚本及资源地址解析服务。上述调研不代表配置已齐备,也尚未修改现有 51 条 pending 资产。
## 本地同步工具已实现(2026-10-16)
原文件已由用户下载到 doodle-assets/cloud_backup。现已新增 [packages/asset-sync](../../packages/asset-sync/README.md) 独立脚本项目,可配置源目录、七牛 AK/SK、统一公开空间与对象前缀。默认预演,显式 --apply 才上传;支持 ETag/大小校验、同内容跳过、同名异内容冲突、有限重试和 JSON 报告。
本地已预演全部 62 个文件及题型预览 51 个文件,未配置真实凭据、未上传七牛,也未更新数据库。此工具只完成“已有备份 → 七牛文件同步”,首次推荐同步预览的命令为 `pnpm assets:sync --apply --only assets/previews`。后续资产回填、用户头像绑定、首页 JSON 重建和旧上传/定时任务切换仍需分别落实。