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。

三层一次配齐,附件这块基本就稳了。