帝国CMS 7.5 附件同步七牛云:PHP插件实现上传删除与批量补传
2026/9/15 21:10:58 网站建设 项目流程

简介:一份专为帝国CMS7.5站点打造的七牛云图片附件管理插件,面向PHP开发者与网站管理员,用于解决图片在本地服务器与七牛云之间的同步上传、删除和分发问题。插件集成了图片自动同步、快速上传、智能删除、CDN加速访问及安全备份等核心能力,可有效减轻主站存储压力,同时借助七牛云分发网络提升图片加载速度与用户体验。压缩包共23个文件,包含22个PHP脚本和1个txt使用说明,整体大小仅35KB,结构精简、易于部署。源码完整覆盖了文件系统操作、curl发起HTTP请求、JSON解析、异常处理以及AccessKey/SecretKey配置管理等关键环节,清晰展示了与七牛云API对接的完整流程与细节。目前已有316人学习浏览,开发者可对照使用说明快速接入帝国CMS7.5,也可将同步、删除逻辑迁移到其他PHP项目中复用,作为学习PHP与云存储集成的实战范例,具有较高的参考价值。

1. 帝国CMS 7.5 的附件塞满服务器时,一个 PHP 七牛云插件能改变什么

服务器上跑着帝国CMS 7.5 的站点,图片越攒越多,d/file 目录动辄几个 GB,备份要连图片一起打包,访问量一上来带宽先被打满。把图片附件搬到七牛云,是最常见的优化动作。这个 zip 插件解开后放到帝国CMS 扩展目录,用 PHP 接管三件事:上传图片时同步推到七牛 bucket,后台删除附件时把七牛对象一起删掉,历史图片用脚本扫目录补传。它解决的是“图片托管到对象存储、又不破坏帝国CMS 后台逻辑”的矛盾。适合跑帝国CMS 老站的运维、改模板的开发者,以及想在项目里做类似同步逻辑的 PHP 工程师。

2. 帝国CMS 7.5 附件流转原理与七牛云双写同步策略

2.1 附件进数据库的路径:从 connect 到 ecms_attach

帝国CMS 7.5 的所有附件管理,最后都会落到 e/class/connect.php 这一层。文章发布、后台编辑器上传、栏目图片设置,走的都是同一套文件处理函数,常见的命名是 AddFile 之类的方法或全局函数。它做的事大致是:接收上传文件、把文件写到 d/file/ 日期目录、把相对路径和文件信息插到附件表。附件表的默认前缀是 phome_ecms_attach,关键字段是 filepath,存的就是 d/file/2024/05/xxx.jpg 这种相对路径。

这个路径就是插件和七牛对象之间的对应关系。七牛那边每个对象用 key 命名,直接把帝国的 filepath 拿来做 key,好处是删除、回源、替换都能一一对应,不用再维护一套映射关系。很多插件翻车就是因为这里多转了一次 hash,后台删本地文件时根本不知道七牛那边该删哪个对象。

在同步逻辑的插入位置上,我一般不会去改帝国自带函数的返回逻辑,而是在文件写入完成之后、函数返回之前挂一个钩子调用,把这个 filepath 传过去。代码层面看起来是这样:

<?php // e/class/connect.php 内部逻辑简化示意 $filepath = AddFileToLocal($file); // 帝国原有写入逻辑 qiniu_sync_after_upload($filepath); // 插件在这里接管同步 return $filepath;

这里的 qiniu_sync_after_upload 干两件事:拼出本地文件的绝对路径,然后调用七牛 SDK 做一次上传。注意这个函数的异常不能往上层抛,否则会影响到帝国后台的上传结果。同步失败只写日志,后续靠批量同步脚本补偿。

2.2 为什么是“同步双写”而不是“上传即删”

标题里写的是“同步”,这个词其实已经圈定了存储策略:本地保留一份,七牛再放一份。另一个思路是上传成功后立刻删除本地文件,只留七牛,叫“本地迁移”。这两者差别决定了插件的整体设计。

帝国CMS 7.5 的管理后台有很多功能依赖本地文件,比如生成缩略图、后台图片裁剪、复制文章时重新生成图片地址。一旦本地文件没了,这些操作会直接报错。双写方案虽然磁盘占用没有减半,但保住了后台的可用性,带宽压力和备份体积仍然移到了对象存储那一边。本地迁移方案更适合图片只发布不修改、后台也不做图片处理的纯展示站。

从 zip 插件常见的交付形态看,双写也是更稳妥的默认选择。它允许你在任何出问题时把附件域名的解析切回来,站点图片一点都不受影响。只要 bucket 里的对象都在,CDN 配好之后,本地磁盘占用的那点空间完全可以用成本更低的存储介质来扛。

2.3 七牛云上传凭证参数与区域选择

七牛的对象存储用上传凭证(upload token)来做身份认证,PHP 端签名时不要把 AK/SK 直接拼进 URL。凭证由 AK/SK、bucket、有效期一起签发。配置项里最容易被搞错的是区域(zone),bucket 创建在哪个区域,上传域名就得用对应区域的上传入口。下面是一张我一般会贴到部署文档里的参数表:

参数含义典型值注意点
bucket七牛存储空间名称empire-attach创建后不能直接在控制台改名
access_key七牛开发者密钥的 AK控制台个人中心获取泄露后任何人可传文件到你的空间
secret_key七牛开发者密钥的 SK控制台个人中心获取不要写死在 PHP 文件里
zone存储区域z0(华东)/ z1(华北)/ z2(华南)和 bucket 实际区域必须一致
upload_host上传域名up.qiniup.com华北是 up-z1.qiniup.com,华南是 up-z2.qiniup.com
expires凭证有效期3600长任务建议用 7200,到期要重新签发

区域一旦配错,上传返回会直接报区域不匹配,错误信息里会带 expected zone 和 current zone。调试时先看这两个字段,别去翻网络问题。改完参数要重新生成上传凭证,因为旧凭证里已包含旧的区域信息。

2.4 插件 zip 包的落盘位置和加载方式

拿到 zip 解开之后放哪,决定了帝国CMS 能不能加载到。常见做法是放到 e/extend/qiuniu_sync/,然后在 e/config.php 底部加一行 require_once。也有人把插件直接丢到 e/class/ 下,靠 connect.php 的 include 逻辑加载,但这样会把第三方 SDK 和帝国自带代码混在一起,升级帝国系统时容易互踩。

另一种方式是用帝国CMS 自带的扩展接口,在后台扩展菜单挂一个管理入口。不过这类图片同步插件通常不需要后台界面,配置项放在一个 qiniu_config.php 里就够。我在生产环境里更倾向把 AK/SK 放到配置文件之外,用 getenv 读取,这样 zip 包被别人拿走后也不会直接泄露密钥。

3. 用 PHP 实现帝国CMS 七牛云图片上传与删除同步插件

3.1 插件目录结构与七牛 SDK 的引入方式

一个能用的最小插件目录,七牛 PHP SDK 不需要 composer,直接下载解压放在插件目录内即可:

qiuniu_sync/ ├── qiniu-sdk/ # 七牛官方 PHP SDK v7 ├── sync.class.php # 上传/删除/日志封装 ├── cli_sync.php # 命令行批量补传 └── qiniu_config.sample.php

SDK 引入方式:sync.class.php 里 require qiniu-sdk/autoload.php。七牛 SDK v7 用的是命名空间,类名是 Qiniu\Auth、Qiniu\Storage\UploadManager 这些,和帝国CMS 老的全局函数不会冲突。我处理过几次老 CMS 集成第三方库的问题,最怕全局类名撞车;SDK 如果不走命名空间,基本可以直接换一个封装版本。

qiniu_config.sample.php 里的配置项,就是上一章那张参数表落地成 PHP 数组,键名保持和表头一致即可。

3.2 上传同步最核心的 putFile 调用

看关键动作。我设计了一个单独的 pushFile 方法,输入是本地绝对路径和七牛 key,返回是否成功。核心就几行:

<?php require_once __DIR__ . '/qiniu-sdk/autoload.php'; use Qiniu\Auth; use Qiniu\Storage\UploadManager; class QiniuSync { private $auth; public function __construct($ak, $sk) { $this->auth = new Auth($ak, $sk); } public function pushFile($localFile, $key) { $token = $this->auth->uploadToken(QINIU_BUCKET, $key, 3600); $uploadMgr = new UploadManager(); list($ret, $err) = $uploadMgr->putFile($token, $key, $localFile); if ($err !== null) { error_log('[qiniu_sync] upload failed: ' . $key . ' ' . $err->message()); return false; } return true; } }

这里有个细节值得展开。uploadToken 的第二个参数传了 $key,意思是这个凭证只允许上传这个名字的对象,更安全;如果你希望一个凭证能传任意 key,可以传 null。putFile 第三个参数可以传附加参数,比如 MIME 类型,PHP 端一般不需要刻意指定,SDK 会按扩展名推。

同步失败时我只打了日志,没有向上抛异常。上传图片如果七牛失败,帝国CMS 那边本地文件已经写好了,文章照常发布,只是 CDN 上暂时没有这张图。这是故意设计的:不能让存储服务故障反向影响 CMS 的业务可用性。空窗期由对账脚本补传。

3.3 删除同步:拦截删除动作并清理七牛对象

删除同步的困难不在调用七牛接口,而在找对删除入口。帝国CMS 7.5 后台删除内容时有一系列清理动作,删除附件图片的函数处理的是相对路径。常见命名是 DelFile 这类函数,里面先记录文件路径,然后 unlink 掉本地文件。

插件的做法是在 unlink 之前,拿到那条相对路径,先去七牛把对象删掉。删除接口用 BucketManager:

<?php use Qiniu\Storage\BucketManager; public function deleteFile($key) { $bucketMgr = new BucketManager($this->auth); list($ret, $err) = $bucketMgr->delete(QINIU_BUCKET, $key); if ($err !== null && $err->code() != 612) { error_log('[qiniu_sync] delete failed: ' . $key . ' ' . $err->message()); return false; } return true; }

612 是七牛“文件不存在”的错误码。后台删除时可能重复触发,或者图片之前同步失败压根没传上去,这时删不到对象不能算失败。我在实际部署时把 612 当作成功处理,只记录一条 debug 日志。

删除顺序上,先删七牛、再 unlink 本地。如果七牛删除超时,本地文件还在,下轮可以重试;反过来本地没了,七牛那边会留下孤儿对象,白白占存储。按这个顺序,最坏情况是本地和七牛短暂不一致,但不会留下无法清理的孤儿。

3.4 历史附件批量同步脚本与断点续传

新插件装上后只能同步新上传的图,老图得靠批量脚本跑一遍。CLI 脚本的思路是遍历 d/file 目录,用七牛的 stat 接口判断对象是否存在,不存在就补传。这里的关键是别把访问统计接口当成存在性判断,stat 返回 612 才表示不存在。

php cli_sync.php --base=/data/www/ecms/d/file --prefix=d/file --sleep=100
<?php // cli_sync.php 核心循环 foreach ($fileList as $rel) { $key = $prefix . '/' . $rel; list($ret, $err) = $bucketMgr->stat(QINIU_BUCKET, $key); if ($err !== null && $err->code() == 612) { $sync->pushFile($base . '/' . $rel, $key); } usleep($sleep * 1000); }

参数解读:--base 是本地 d/file 的绝对路径,--prefix 是七牛对象名的前缀,--sleep 是每传一个文件后的等待毫秒数。深夜里跑我把 sleep 调到 200,白天调小到 20,控制磁盘读 IO 对线上服务的影响。

注意:批量脚本不要在业务高峰跑,sleep 参数按磁盘 IO 压力调,拿不准就等晚上执行。

断点续传不是在代码里做复杂状态机,而是记录一个游标文件。脚本每处理 500 个文件就把最后一个相对路径写到 sync_offset.txt,重新启动时从文件恢复遍历位置。对于十万张以下的老站,这比在数据库里维护同步状态表简单得多。

4. 七牛云 bucket 配置与帝国CMS 图片域名替换的排错清单

4.1 附件域名替换:改模板输出而不是改数据库

插件只负责同步,前台访问要走 CDN 或七牛默认域名。帝国CMS 7.5 文章内容里存的是 d/file/xxx.jpg 这种相对路径,完整 URL 是在模板里拼的。所以域名替换最容易做的就是模板替换函数。

我把这个函数放到帝国CMS 的自定义函数文件里:

<?php function qn_asset($src) { if ($src === '' || strpos($src, 'http') === 0) { return $src; } return 'https://img.example.com/' . ltrim($src, '/'); }

模板里原来输出图片地址的地方,套一层 qn_asset。为什么不直接 UPDATE 数据库把 d/file 换成完整域名?因为帝国后台的附件管理、缩略图、相关文章功能都拿相对路径做拼接,数据库一改,这些逻辑全都会拼出畸形 URL。改模板是回退最快、影响面最小的方案。

4.2 批量同步脚本和 CDN 预热的关系

同步完历史图片之后,很多人忽略一个问题:文件传到七牛了,但 CDN 节点上还没有缓存,第一次访问会回源,刚好把突发的回源流量打回源站。对于图片量比较大的站,正确顺序是先跑同步,再通过七牛控制台做批量刷新预热,或者单独写一个预热脚本去请求一次 URL。

预热脚本本质上就是一个带 wget 的循环。生产环境里我一般直接用七牛控制台导入 URL 列表批量提交,简单可靠,数量在五万以内控制台一把梭就行,数量更大再考虑拆文件分批提交。

4.3 上传失败的常见报错与处理顺序

把排查顺序固定下来,能省掉大量时间。先看报错代码,再决定查什么。下面是速查表:

报错代码/现象可能原因处理方式
401 bad tokenAK/SK 配错或凭证过期重新生成 token,检查配置文件
incorrect region上传域名与 bucket 区域不一致按区域换 up-z1 / up-z2 域名
612 object not found删除或 stat 的对象不存在按业务视为成功,记录日志
403 forbiddenbucket 私有或防盗链拦截设置公开读,或配置 Referer 白名单
connect timeout服务器出口到七牛网络不稳增加重试,标记为待补传

4.4 PHP 上传安全的一处提醒

同步插件的安全边界是整个上传入口。帝国CMS 7.5 本身有扩展名白名单,但 PHP 环境里解析漏洞和 MIME 伪造的情况还是要防。插件在把文件推到七牛前,我会多做一次真实性校验,用 getimagesize 判断图片文件头。如果文件是脚本伪造的图片马,getimagesize 会直接返回 false,这时就不该向七牛同步,还应该拦下这次上传操作。

提示:上传接口的 MIME 校验不要信任前端 Content-Type,PHP 端用 getimagesize 看文件头更可靠。

5. 进阶:用七牛云图片处理样式替代帝国CMS 本地缩略图逻辑

5.1 为什么本地缩略图逻辑在迁移后不再好使

双写方案里,新上传图片时本地和七牛都有原图。帝国CMS 7.5 的内容模型里有小图字段,后台生成缩略图会调用本地 GD 库裁一张。本来这没问题,但历史图片同步时段里,如果本地磁盘有清理策略,或者服务器换过机器,老图的本地文件已经不在,后台再生成缩略图就会失败。

更干净的做法是不再依赖帝国那套本地裁剪,把“图片生产”这一步完全交给七牛云的 imageView2 接口。它是在 URL 上加参数实时处理,不占服务器 CPU。

5.2 模板里接一个 qn_thumb 函数

<?php function qn_thumb($src, $w = 400, $h = 300, $q = 80) { $url = qn_asset($src); return $url . '?imageView2/1/w/' . $w . '/h/' . $h . '/q/' . $q; }

调用时把模板里的图片地址替换掉。比如列表页原本输出小图字段,改成 qn_thumb(原图地址, 360, 240)。imageView2/1 表示按宽高强制裁剪,适合列表缩略图;如果想要等比缩放不要裁剪,把 /1 换成 /0。质量 q 我一般设 80,再低肉眼难看出差别,体积能省 30% 左右。

内容页的原图也可以走这个函数,只要把 w 参数设成大尺寸,比如 w/1200。这样服务器上全程不需要再跑一遍 GD 库,新图旧图统一走 CDN 的实时处理节点。

5.3 让图片处理在边缘节点完成而不是源站

最后这步是关键:七牛云 imageView2 实时处理默认由存储侧执行。在 CDN 域名上开启图片处理后,可以把样式参数直接放在 CDN 域名后面,让边缘节点返回处理完的图,源站只出原图。验证方式很简单,curl 一下带样式的 URL,看响应头里是否有 CDN 缓存命中的标记,有就说明处理后的图已经在边缘节点落下来了。这样最耗 CPU 的图片缩放被完全隔离开,帝国CMS 本地的缩略图字段即使为空也不影响前台展示。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询