migrate_private_storage工具使用说明 [复制链接]

管理员组

这个工具 是用来把存量敏感附件(zip/pdf/待审帖附件、应用下载包等)从公开的 upload/attach/ 搬到私有存储,并把帖子正文里的旧 URL 改写成鉴权链接。CLI 专用,Web 访问会 403。

执行顺序(照着走)

  1. 先加 Nginx deny(顺序不能反,否则搬过去等于又暴露)

    location ^~ /upload/private/ { deny all; }
    

    重载 nginx,访问 /upload/private/ 应 403。

  2. 备份(这一步没法靠重跑撤销)

    cd /www/wwwroot/xiunox.org
    mysqldump -u root -p 库名 post > /tmp/post_backup.sql
    tar czf /tmp/attach_backup.tgz upload/attach
    
  3. dry-run,先看要搬多少

    sudo -u www php tool/migrate_private_storage.php
    

    只统计不改文件,输出 需保护附件 N 和抽样 20 条 [need]。正常图片/视频不会出现在列表里。

  4. 确认无误后执行

    sudo -u www php tool/migrate_private_storage.php --apply
    
    • 每条:复制到 upload/private/attach/ → SHA-256 校验 → 删公开副本 → 改写 post.message/message_fmt 里的旧 URL;

    • 输出 [migrated] aid=... 文件名(前 20 条 + 每 500 条)、失败打 [FAILED] 原因;

    • 幂等,失败的可单独处理后重跑,已迁移的自动跳过。

    ⚠️ 必须用 sudo -u www(宝塔的 web 用户)跑。用 root 跑会创建 0700 且属主 root 的目录,PHP 读不了,全站附件下载会挂。已经用 root 跑过的话补救:chown -R www:www upload/private。

  5. 收尾

    • 后台「其他 → 缓存」清空全站缓存(列表缓存可能还留着旧静态 URL);

    • CDN 侧清 upload/attach 历史缓存;

    • 抽查:随便找一个已迁移的 zip,直链 https://.../upload/attach/xxxx.zip 应 403/404,站内下载/图片显示正常。

看输出怎么判断

  • state: migrated → 已搬并删了公开副本;

  • state: private → 之前就在私有目录,跳过;

  • [FAILED] ... hash mismatch / public copy could not be safely removed → 文件被占用或权限问题,处理后可重跑;

  • 末尾 upload/tmp 下遗留敏感文件 N 个 → 只统计 .meta.json/压缩包残留,Web 已拦截,可手工删。

可选:想保留"Web 根之外"的强保护

先建 conf/private_storage.php:

<?php
return array('path' => '/data/xiunox-private');   // 绝对路径,web 根之外

并把该路径加进 open_basedir,然后再跑上面第 3、4 步(迁移前必须先确定路径,否则已搬的文件会对不上)。不做这一步就用默认 upload/private/(弱保护 + Nginx deny)。

小提醒:迁移期间在线的帖子里旧图片 URL 会有几分钟的 404 窗口(文件已删、缓存还没清),建议低峰期做。

最新回复
  • xiunox 一级用户组

    这篇说明写得很扎实,步骤顺序和权限坑都点到了。补充三点供参考:

    1. 减少 404 窗口:建议把流程改成「复制 → 校验 → 改写 URL → 清缓存 → 再删公开副本」,即删文件挪到最后并留一个 --purge 子命令。这样即使中途失败,站内也是先可用后收紧,不必卡低峰期。

    2. URL 匹配要宽容:post.message 里可能是 /upload/attach/x.zip、带域名的绝对地址、以及 message_fmt 里已 HTML 转义的形态。建议 dry-run 时按正则分组统计命中数,避免只替换到一半、重跑时又因幂等跳过而未修正。

    3. 幂等边界:若 message 与 message_fmt 只成功改写其一就中断,重跑会因文件已是 private 而跳过改写。可在 private 分支里补一次「URL 兜底检查」再决定是否标记完成。

    另外建议把 [FAILED] 的原因码做成可 grep 的固定前缀,便于批量排查。【 AI 自动回复,仅供参考】

    2小时前

请先登录后再回复 登录