3.4 附件上传限制与存储优化
附件就像快递柜:格子尺寸决定能塞多大件,每天每户取件有上限,物业还得操心柜子放哪儿、满了怎么办。Discuz! X5.0 里这套「柜子管理」分三层——后台限额、PHP 运行环境、磁盘与存储后端。
后台附件限制:大小、类型、每日、每用户
专有名词:后台附件设置在管理中心「全局 → 附件设置」,管文件类型、单文件大小、每日与每用户上限,由 Discuz 自己校验。
| 设置项 | 含义 | 建议值 |
|---|---|---|
| 附件最大尺寸 | 单文件最大字节数(KB) | 图片 2048,压缩包 10240 |
| 允许的文件类型 | 按扩展名放行,如 jpg,gif,png,zip,pdf |
按业务收口,别全开 |
| 每天最大附件数 | 单用户每日可上传数量 | 普通 20,会员 100 |
| 每用户最大附件数 | 账户生命周期累计上限 | 视磁盘容量定 |
| 附件总空间限制 | 单用户占用附件目录总容量 | 普通 50M,会员 500M |
「附件最大尺寸」是 Discuz 软上限,挡不住 PHP 先一步拒掉大文件。所以下一步必须同步 PHP。
PHP 层上传参数:upload_max_filesize 与 post_max_size
X5.0 要求 PHP 8.0+(企飞版 X5.1 要 8.2+),上传配置在 php.ini。一句话记:post_max_size 是整包,upload_max_filesize 是单文件,memory_limit 需最大。
| 指令 | 说明 | 推荐值 |
|---|---|---|
upload_max_filesize |
单文件上传上限 | 与后台对齐,如 10M |
post_max_size |
一次请求总数据上限 | 略大,如 12M |
memory_limit |
PHP 进程内存 | 至少 16M,建议 128M |
max_execution_time |
脚本最长秒数 | 大文件 300 |
max_input_time |
接收输入最长时间 | 300 |
max_file_uploads |
单次最多上传文件数 | 20 |
改法(路径以你的版本为准):
; /usr/local/php/conf/php.ini 或 php --ini 查到的路径
upload_max_filesize = 10M
post_max_size = 12M
memory_limit = 128M
max_execution_time = 300
max_input_time = 300
max_file_uploads = 20
改完重载 PHP:
php -i | grep -E "upload_max_filesize|post_max_size|memory_limit"
systemctl reload php-fpm # PHP-FPM 模式
# apache 用户改为:systemctl reload httpd
附件存储路径与磁盘规划(data/attachment)
X5.0 用 MitFrame 框架,附件默认落在 data/attachment,这是「柜子本体」。规划要点:
- 把
data/attachment单独挂大容量盘(云盘),别和系统盘挤。系统盘满网站直接 500,附件盘满只是传不上图。 - 目录按年月分桶,方便迁移。
- 定期
df -h盯使用率,超 80% 就该扩容。
把 /path/to/your/site 换成站点根目录(即 DISCUZ_DATA 指向的 data):
df -h /path/to/your/site/data/attachment
du -sh /path/to/your/site/data/attachment/forum/*
find /path/to/your/site/data/attachment -type f -size +50M -exec ls -lh {} \;
对象存储:用插件接管附件落地(X5 新机制)
X5 把对象存储、邮件等组件改成「插件接管」方式。专有名词:child(子文件)机制——每个 App 目录有 child/ 子目录,系统自动引用其中文件;插件注册接管后可完全代替原系统逻辑,是 X5 最强扩展点。
落地思路(SDK 以你的版本为准,别照抄类名):
<?php
if(!defined('IN_DISCUZ')) exit('Access Denied');
function myoss_put($tmp, $object) {
// 调用云厂商 SDK 上传,方法名以你的插件为准
}
function myoss_upload($file) {
global $_G;
$tmp = $file['tmp_name'];
$object = 'attach/'.date('Ym').'/'.md5($tmp.time());
myoss_put($tmp, $object);
return $object;
}
放进插件 source/plugin/{标识}/ 配合 child 机制接管;DB::insert 是 Discuz 实际 API,表 forum_attachment 属 pre_forum_*。
接管方式对比:
| 方式 | 适用场景 | 说明 |
|---|---|---|
| 插件 + child 接管 | 全新部署、彻底换存储 | 最干净,X5 推荐 |
| 插件钩子改写 | 已有站点小改动 | 改动小但耦合深 |
| 外部存储挂载 | 保留本地路径写法 | 把盘挂成对象存储,改动最少 |
企飞版 X5.1 内置对象存储与队列,开源版 X5.0 靠插件接管实现。
附件水印与缩略图
图片附件常做水印和缩略图,都在「全局 → 附件设置 → 水印 / 缩略图」里开。
- 水印建议右下角、透明度 30%~50%,太重影响观感。
- 缩略图尺寸按版块宽度定,常见 120×120 或 200×150,生成后原图保留。
- 开启后只对开启后新图生效,存量图要写脚本批量跑。
模板侧,X5 默认启用 .php 后缀模板文件,可用 `{eval echo getforumimg($attach['aid'], 0, 200, 150)}` 直接输出缩略图 URL。getforumimg 参数依次是附件 ID、类型、宽、高,签名以你的版本为准。
附件迁移:从本地盘搬到对象存储
迁移思路:把 data/attachment 文件上传对象存储,再把 attachment 字段改成「对象存储 key」,最后用 child 接管让新上传也走对象存储。
# 以阿里云 ossutil 为例,命令以你的云厂商为准
ossutil cp -r /path/to/your/site/data/attachment oss://your-bucket/attachment --update
find /path/to/your/site/data/attachment -type f | wc -l
数据库侧改本地路径前缀(先备份,表前缀 pre_ 以你的为准):
UPDATE pre_forum_attachment SET attachment = CONCAT('attachment/', attachment)
WHERE attachment NOT LIKE 'attachment/%';
迁移后抽样打开几篇带图帖子确认前台能正常加载。
附件清理:过期、孤立与违规文件
附件会悄悄膨胀,定期清理是必修课,三类要清:
- 孤立文件:数据库没记录的磁盘文件,用
pre_forum_attachment的attachment反查磁盘,对不上的删。 - 违规文件:被举报下架但磁盘还在的,按审核队列
pre_moderation_*处理。 - 过期临时文件:
data/attachment下临时缓存,确认无用后清。
# 先列出来人工确认,别直接删
find /path/to/your/site/data/attachment -type f -mtime +90 -print
# 确认无误再删:find ... -mtime +90 -delete
DELETE FROM pre_forum_attachment
WHERE tid NOT IN (SELECT tid FROM pre_forum_thread);
pre_forum_thread 是否真实存在以官方数据字典为准,执行前先 SELECT 数影响行数。
常见疑难速查
- 改了 php.ini 仍报文件过大:八成没重载 PHP,或改错文件。
- 前台裂图但后台有记录:附件盘满、权限不足、或迁移后路径没对齐。
- 水印不生效:只对开启后新图生效,存量需批量重生成。
- 对象存储上传慢:检查 child 接管是否每次走外网,大站建议用内网 endpoint。
三层一次配齐,附件这块基本就稳了。
