这个工具 是用来把存量敏感附件(zip/pdf/待审帖附件、应用下载包等)从公开的 upload/attach/ 搬到私有存储,并把帖子正文里的旧 URL 改写成鉴权链接。CLI 专用,Web 访问会 403。
执行顺序(照着走)
先加 Nginx deny(顺序不能反,否则搬过去等于又暴露)
location ^~ /upload/private/ { deny all; }重载 nginx,访问
/upload/private/应 403。备份(这一步没法靠重跑撤销)
cd /www/wwwroot/xiunox.org mysqldump -u root -p 库名 post > /tmp/post_backup.sql tar czf /tmp/attach_backup.tgz upload/attachdry-run,先看要搬多少
sudo -u www php tool/migrate_private_storage.php只统计不改文件,输出
需保护附件 N和抽样 20 条[need]。正常图片/视频不会出现在列表里。确认无误后执行
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。收尾
后台「其他 → 缓存」清空全站缓存(列表缓存可能还留着旧静态 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 窗口(文件已删、缓存还没清),建议低峰期做。



