跳到主要内容

存量文件迁移指引

本文介绍如何把旧版 SDK 上传的文件迁入云存储桶,让它们恢复存储桶归属。

适用对象

在 PG 环境中使用过旧版 SDK(JS SDK V2 / 旧 Node SDK / 旧 wx-server-sdk)上传文件,且控制台「云存储 - 存储管理」页顶部提示「旧版 SDK 上传的文件不归属于任何存储桶」。

开始之前​

什么是「存量文件」:使用旧版 SDK 上传、目前未归属任何存储桶的文件。它们的字节已经在环境关联的 COS 桶里,只是缺了「归属」这一步,因此在控制台按存储桶查看时看不到,也不受存储桶的权限与策略管控。

迁移这件事本身,你可能会关心:

你可能想问回答
会影响线上业务吗不会。迁移只读取源端文件、向目标存储桶写入新对象,不改动也不删除已有文件(源文件默认保留)
需要停服吗不需要,可在线执行
大概要多久取决于文件数量与总大小。脚本默认并发 4(可用 --concurrency 调整),支持断点续跑,中断后重跑会接着传
会产生额外费用吗迁移期间同一份数据会短暂存在两份(源端原文件 + 桶内副本)。确认无误后执行 cleanup 清理源端即可恢复单份
业务代码要改路径吗不需要。迁移不改变文件的对象名(key),只是补上存储桶归属
升级 SDK 后还需要迁移吗需要。升级只让新上传的文件走存储桶链路,已上传的文件不会自动迁移
建议

先在测试环境用小范围数据(--source-prefix)跑通全流程,再对生产环境全量迁移。

你需要做的两件事​

你要做的解决什么现在不做会怎样
升级 SDK让「新上传的文件」走存储桶链路直传链路封禁后,上传请求会被直接拒绝
迁移存量文件(本指引)让「已经传上去的文件」恢复归属文件在控制台按桶不可见、不受桶策略管控

升级路径:JS SDK V3(推荐) | Node SDK V4.1 | wx-server-sdk(小程序)

迁移总览​

体检 ──► 识别 ──► 迁移 ──► 校验 ──► 收口
工具/凭证/ 列出待迁移 写入目标桶 确认全部归入 确认后可清理
源端连通 的文件 且策略生效 源端、关掉提示

迁移的本质:文件的字节已经在环境的 COS 桶里,缺的是「归属」。迁移就是按官方网关把它们重新写入指定存储桶,让字节落位与元数据登记同时成立。

本指引配套迁移脚本,请先下载:下载 pg-bucket-migration.zip(含迁移脚本与配置模板,解压即用,Node.js ≥ 18,零 npm 依赖)。

下文命令均在解压后的 pg-bucket-migration 目录中执行。

前置准备​

  1. 确认目标存储桶已存在。在控制台「云存储 - 存储管理」创建或选定目标桶(下文以 bucket1 为例):

    tcb storage buckets list -e <envId>
  2. 登录 tcb CLI(迁移默认走 CLI 后端):

    tcb login # 推荐:交互式授权登录,密钥不落到命令行
    注意

    如需非交互登录(CI 场景),可用 tcb login --apiKeyId <SecretId> --apiKey <SecretKey>。这种方式会让密钥出现在 shell 历史与进程列表中,建议改用临时密钥,并在迁移结束后及时禁用。

  3. 安装 rclone(用于枚举与拉取源端文件):

    # macOS
    brew install rclone
    # Linux
    curl https://rclone.org/install.sh | sudo bash
    # Windows: scoop install rclone
  4. 填写配置:

    unzip pg-bucket-migration.zip && cd pg-bucket-migration
    cp .env.example .env
    vim .env # 填 CB_ENV_ID / CB_TARGET_BUCKET / COS 凭证或 CB_SOURCE

权限要求:源端 COS 桶的只读权限、目标存储桶的读写权限。

密钥安全

.env 含明文密钥,已加入 .gitignore,请勿提交到仓库或外发。

步骤 1:迁移前体检​

先确认工具、凭证、源端连通性、接口都正常,避免跑到一半才发现问题:

node migrate-orphaned-to-bucket.mjs check
── 体检结果 ───────────────────────────────
rclone : 可用
源端可访问 : cbstorage-src:<bucket-APPID>(1,247 个对象,2.31 GB)
envId : my-env-id
目标存储桶 : bucket1
存储后端 : tcb CLI(可用,桶:bucket1)
桶内对象列表 : 正常(当前 402 个对象)
───────────────────────────────────────────

任一项异常就先按提示修好,再进入下一步。

步骤 2:识别待迁移文件​

脚本用「源端文件全集」减去「已归属的文件」,得到待迁移清单。这一步只读,不写入任何数据,可以反复执行。

node migrate-orphaned-to-bucket.mjs plan
→ 枚举源端存量文件(COS)…
源端文件:1,247 个,合计 2.31 GB
→ 列出已归桶对象(pg 存储桶「bucket1」)…
已归桶:402 个

── 迁移预览 ───────────────────────────────────
待迁移(未归桶):845 个,1.86 GB
已归桶(跳过) :402 个
已归桶物理对象 :402 个(已归属其他存储桶,自动跳过)
清单已写入 :.pg-bucket-migration/manifest.json
───────────────────────────────────────────────

「已归桶物理对象」是已归属存储桶的对象(在 COS 中的路径带桶 ID 前缀)。脚本会自动识别并跳过,无需人工处理。

先看清单,再决定怎么迁。 常用参数:

情况处理方式
只想迁某个目录--source-prefix uploads/
想排除某些文件--exclude "tmp/**"
源端路径带了多余前缀,想剥掉--strip-prefix legacy/
个别大文件想单独处理--max-file-size 104857600(跳过 >100MB 的)

步骤 3:执行迁移​

node migrate-orphaned-to-bucket.mjs migrate --yes

脚本逐个处理:拉取源端文件 → 通过官方网关写入目标桶 → 记录结果 → 继续下一个。

将把 845 个文件(1.86 GB)迁移进存储桶「bucket1」
策略:reupload
✓ [1/845] uploads/avatar-001.jpg (1.24 MB)
✓ [2/845] docs/manual.pdf (3.80 MB)
✓ [3/845] images/banner.png (2.10 MB)
…
── 迁移结果 ───────────────────────────────
成功:845 / 845 失败:0
───────────────────────────────────────────

特性:

  • 可断点续跑:中断或部分失败后重跑同一条命令,自动跳过已成功的文件
  • 幂等:目标桶已存在同名对象时自动覆盖,重跑不会报错
  • 默认并发 4,可用 --concurrency 8 调整
  • 默认保留源端文件(见可选:回收源端空间)

迁移不会改变文件的对象名(key)。原来叫 uploads/avatar-001.jpg,归入存储桶后仍是这个名字,业务代码无需改路径。

步骤 4:校验结果​

node migrate-orphaned-to-bucket.mjs verify
── 校验结果 ───────────────────────────────
清单文件数 :845
桶内对象总数 :1,247
缺失(未归桶) :0
大小不一致 :0
───────────────────────────────────────────

✅ 全部待迁移文件已归入存储桶。

校验通过后,再到控制台「云存储 - 存储管理」做最后确认:

  • 在目标存储桶下能看到这批文件
  • 文件大小、路径与迁移前一致
  • 存储桶的访问权限、策略、文件大小限制、允许文件类型对它们已生效(可按策略试读/试写验证)
  • 在测试环境验证一遍业务读写

若还有缺失或大小不一致,重跑 migrate(自动跳过已成功项)后再 verify。

步骤 5:关掉控制台提示​

待迁移文件全部归入存储桶后,把控制台「存储管理」页顶部的提示条收口:

node migrate-orphaned-to-bucket.mjs close-banner

脚本会打印具体操作方式,按提示将环境标记 postgresql_orphaned_storage 置为 false 即可。

注意

只有当环境确实不再存在未归属文件时才执行,否则会误关掉其他使用者的提示。可先跑一次 plan,确认「待迁移」为 0。

可选:回收源端空间​

迁移默认保留源端文件,便于随时回退。但同一份数据会占两份空间,确认业务正常后建议清理:

# 先预览会删哪些文件
node migrate-orphaned-to-bucket.mjs cleanup --dry-run

# 确认后执行
node migrate-orphaned-to-bucket.mjs cleanup
── 源端清理预览 ───────────────────────────
可清理(桶内已存在同名对象):845 个,1.86 GB
- uploads/avatar-001.jpg
- docs/manual.pdf
…
跳过(桶内未找到同名对象,保持不动):0 个
───────────────────────────────────────────

安全性:cleanup 只删除已记录为迁移成功、且目标桶内确实存在同名对象的源端文件,其余一律跳过。逐条执行并即时记录,中断后可安全重跑。

也可以在迁移时顺带清理:node migrate-orphaned-to-bucket.mjs migrate --delete-source。

常见问题​

迁移相关​

问题回答
升级了 SDK,还需要迁移吗需要。升级只管新上传的文件,已上传的不会自动迁移
迁移后业务要改代码吗不需要。对象名不变,只是补上存储桶归属
文件很多(>10GB)怎么办可按目录分批(--source-prefix),或参考控制台的大批量导入工具
迁移中途中断了怎么办直接重跑同一条 migrate 命令,已成功的文件会被跳过
迁移后能回退吗能。源端文件默认保留,删除目标桶内已迁入的对象即可

报错处理​

现象原因处理
未找到 rclone未安装或不在 PATHbrew install rclone,或用 RCLONE_BIN 指定路径
403 PathStyleDomainForbiddenCOS 只支持 Virtual Hosted-Style 寻址确认 rclone 配置含 force_path_style=false
STORAGE_KEY_ALREADY_EXISTS目标桶已有同名对象,接口默认拒绝覆盖脚本已自动处理;手工操作时加 --upsert
STORAGE_BUCKET_NOT_FOUND目标存储桶不存在先在控制台创建存储桶
STORAGE_PERMISSION_DENIED存储桶的 RLS 策略未放行配置存储桶 RLS 策略后重试
待迁移清单明显偏多源端混着非业务文件用 --exclude 排除
部分文件上传失败网络抖动或单文件过大重跑 migrate 自动补传;或调小并发、调大 --retries

安全与回滚​

  • 先小范围验证:用 --source-prefix 选一个子目录跑通全流程,再全量迁移。
  • 默认不删源文件:源端文件保持原样,随时可回退。
  • 权限最小化:只授予源端桶只读、目标桶读写权限;迁移结束后及时禁用或删除所用密钥。
  • 回滚方式:迁移不破坏源数据。如需回退,删除目标桶内已迁入的对象即可(tcb storage objects rm <key> --bucket <桶>)。

附录 A:脚本包目录说明​

下载的 pg-bucket-migration.zip 解压后是一个目录,构成如下:

pg-bucket-migration/
├── migrate-orphaned-to-bucket.mjs
├── .env.example
├── README.md
├── .gitignore
└── 存量文件迁移指引.md
文件作用是否必需
migrate-orphaned-to-bucket.mjs迁移脚本主程序,包含体检 / 识别 / 迁移 / 校验 / 收口全部逻辑,零 npm 依赖必需
.env.example配置模板,复制为 .env 后填写环境与凭证,避免手拼环境变量建议
README.md目录说明、依赖要求与命令一览,解压后第一眼就能看到建议
.gitignore忽略 .env 与状态目录,防止密钥被提交进你的仓库建议
存量文件迁移指引.md本指引,离线可查可选

运行环境要求:Node.js ≥ 18、rclone;使用默认的 CLI 后端时还需要 tcb CLI。环境准备见前置准备。

首次运行后,脚本会在同目录生成状态目录 .pg-bucket-migration/(可用 --state-dir 或 CB_MIGRATION_DIR 改名):

.pg-bucket-migration/
├── manifest.json
├── state.json
├── report.json
└── verify.json
文件由哪个命令产生作用
manifest.jsonplan待迁移文件清单与跳过统计
state.jsonmigrate逐文件结果,断点续跑的依据
report.jsonmigrate本轮成功 / 失败汇总与失败明细
verify.jsonverify校验结论:缺失数、大小一致性、是否通过

状态目录只记录迁移结果,不含密钥,可随时删除。删除后下次运行会重新生成,代价是丢失断点续跑能力与迁移留痕。

附录 B:不使用脚本的手工迁移​

文件很少(个位数)、或不方便运行脚本时,也可以纯手工完成。

# 1. 查看目标存储桶与桶内对象
tcb storage buckets list -e <envId>
tcb storage objects list --bucket bucket1 -e <envId>

# 2. 查看源端文件(rclone 直连环境 COS 桶,注意 force_path_style = false)
rclone ls cbstorage-src:<bucket-APPID>

# 3. 逐个写入目标存储桶(--upsert 允许覆盖同名对象)
rclone copyto "cbstorage-src:<bucket-APPID>/uploads/avatar-001.jpg" ./tmp.jpg
tcb storage objects upload ./tmp.jpg "uploads/avatar-001.jpg" --bucket bucket1 -e <envId> --upsert
注意

手工方式没有差集识别、断点续跑与校验,文件一多就容易出错,不建议用于批量迁移。