概述
Xiuno X 的附件管理系统通过 AttachmentService 统一服务类处理文件上传、存储、校验和清理全流程。上传过程采用双重安全校验机制——先通过 finfo_file 获取真实 MIME 类型与白名单比对,再结合扩展名白名单过滤,有效防止 .php 伪装成 .jpg 等恶意上传行为。文件统一存储在 upload/attach/ 目录,采用随机文件名避免路径遍历攻击,同时支持图片缩略图自动生成和视频元数据提取。
站长指南
配置入口
后台 → 安全设置 → 上传安全,核心配置项包括:
配置项说明
配置键 | 默认值 | 说明 |
|---|---|---|
| 10485760 (10MB) | 图片最大上传体积 |
| 104857600 (100MB) | 视频最大上传体积 |
| 20971520 (20MB) | 音频最大上传体积 |
| 20971520 (20MB) | 其他文件默认上限 |
| 1 | 是否启用缩略图生成 |
| 200 | 缩略图最大宽度(高度自适应) |
| 1 | 严格 MIME 校验模式,设为 0 启用兼容模式 |
使用场景
帖子附件:发帖时上传图片/视频/文档,系统自动生成缩略图
头像上传:用户中心头像上传,仅允许图片格式
视频嵌入:视频上传后自动提取宽高、时长等元数据
批量导入:通过
AttachmentService::uploadFile()接入自定义上传流程
注意事项
PHP 扩展依赖:缩略图生成需要 GD 扩展(
imagecreatefromjpeg等函数),视频信息提取建议安装ffprobe或getID3插件目录权限:
upload/attach/和upload/tmp/目录必须可写(建议 755 或 777)MIME 校验降级:当 PHP 未启用
fileinfo扩展时,security_upload_strict_mime=1(默认)会拒绝所有上传;如需兼容请设为 0 并安装fileinfo扩展临时文件清理:临时文件存储在
upload/tmp/下,正式发布后会迁移至upload/attach/,未使用的临时文件建议定期清理
开发者指南
核心服务类
类:AttachmentService(service/AttachmentService.php)
方法 | 参数 | 返回 | 说明 |
|---|---|---|---|
| 文件数组、用户ID、选项数组 |
| 图片专用上传,自动生成缩略图 |
| 文件数组、用户ID、选项数组 |
| 视频专用上传,自动提取宽高和时长 |
| 文件数组、用户ID、选项数组 |
| 通用文件上传,自动识别类型 |
| 文件数组、选项数组 |
| 兼容旧接口,支持 |
| 类型分类 |
| 获取某类型的大小限制 |
| 临时文件路径、MIME白名单 |
| 私有方法,校验真实 MIME |
| 原图路径、配置、宽高 |
| 静态方法,生成缩略图 |
| 视频文件路径 |
| 静态方法,获取视频元数据 |
钩子点
上传前校验:通过
security_upload_strict_mime配置控制 MIME 校验严格程度类型白名单扩展:修改
conf/attach.conf.php中的类型数组可扩展允许的文件格式大小限制覆盖:通过
conf配置键upload_max_{type}_size动态覆盖默认限制
扩展方式
自定义上传驱动(如 OSS 云存储),通过 AttachmentService::upload() 的 driver 参数扩展:
// 示例:扩展本地存储逻辑
$service = AttachmentService::getInstance();
$result = $service->uploadFile($_FILES['attachment'], $uid);
// 校验结果
if ($result['code'] === 0) {
$url = $result['message']['url'];
$filetype = $result['message']['filetype'];
}代码示例
1. 前端 FormData 上传对接:
const fd = new FormData();
fd.append('attachment', fileInput.files[0]);
fetch('/api/attachment/upload', {
method: 'POST',
body: fd
}).then(res => res.json()).then(data => {
if (data.code === 0) {
console.log('文件URL:', data.message.url);
console.log('缩略图:', data.message.thumb_url);
}
});2. 自定义类型校验:
// 添加自定义文件类型到白名单
$custom_types = array_merge(
include APP_PATH.'conf/attach.conf.php',
array('apk', 'ipa')
);
// 将扩展后的数组通过 upload_allowed_file_types 配置传入3. 安全文件操作(插件内):
// 使用 xn_safe_io 在白名单路径内操作
xn_safe_write(APP_PATH.'upload/tmp/'.$filename, $content, 'myplugin');
xn_safe_unlink(APP_PATH.'tmp/cache.tmp', 'myplugin');常见问题
上传时提示"文件类型不允许"怎么办? 检查
conf/attach.conf.php是否包含目标扩展名;同时确认security_upload_strict_mime不为 1 且 PHP 已启用fileinfo扩展,否则 MIME 校验会拒绝上传。图片上传后没有生成缩略图? 确认 GD 扩展已安装(
phpinfo查看),且upload_thumb_enabled配置为 1;此外原图尺寸小于配置的最大宽高时不会生成缩略图。视频上传后时长显示为 0? 需安装
ffprobe到系统 PATH 或在plugin/getid3/目录下放置getid3.php,两者都不可用时视频元数据将无法提取。如何清理未使用的临时文件? 临时文件存放在
upload/tmp/下,可编写定时任务扫描超过 24 小时的文件并删除;正式附件存储在upload/attach/下,仅在帖子删除时通过AttachmentService::deleteAttachment()清理。安全模式和兼容模式有什么区别? 严格模式(默认)要求
fileinfo扩展必须可用,通过真实 MIME 与白名单比对;兼容模式在fileinfo不可用时,图片改用getimagesize()降级校验,非图片仅依赖扩展名白名单,安全性较低但兼容性更好。

