简介
本技术指南围绕 DouPHP 的动态资源处理能力,系统阐述用户上传文件的存储机制、临时文件生命周期管理、日志组织、缓存设计、安全控制与大文件优化方案。文档以代码为依据,聚焦附件服务、文件系统抽象与分块上传处理器等核心实现,帮助开发者在业务中正确、安全、高效地处理动态资源。
项目结构
DouPHP 将“动态资源”相关能力集中在以下位置:
- 附件领域服务:负责上传、草稿、认领、URL 解析、画廊聚合等
- 文件系统抽象:统一磁盘配置、路径归一化与驱动扩展点
- 分块上传处理器:实现大文件分片合并、状态记录与落库
- 配置中心:集中定义默认上传策略、允许扩展名、大小限制与缩略图策略
- 门面与入口:对外暴露 attachment() 静态调用方式,简化业务接入
graph TB
A["前端/小程序"] --> B["控制器<br/>如 UserController"]
B --> C["附件门面 Attachment"]
C --> D["附件服务 AttachmentService"]
D --> E["分块处理器 ChunkedUploadHandler"]
D --> F["文件系统管理器 FilesystemManager"]
F --> G["磁盘 Disklocal"]
D --> H["数据库 dou_file"]
D --> I["图片处理 ImageManager"]
核心组件
- 附件服务 AttachmentService:封装上传、草稿、认领、内容拉图、URL 生成、画廊查询等完整流程;内置类型校验、大小限制、水印与缩略图生成;支持分块上传与草稿认领。
- 分块上传处理器 ChunkedUploadHandler:接收分片参数,按顺序合并为完整文件,写入磁盘并持久化元数据;支持草稿模式与拥有者身份标记。
- 文件系统管理器 FilesystemManager:根据命名约定与配置文件解析磁盘根、URL 前缀与上传策略;提供 build 动态构造磁盘的能力。
- 配置 file.php:集中声明默认磁盘、上传最大尺寸、允许扩展名、图片质量与缩略图子目录等。
- 门面 Attachment:通过静态方法暴露附件服务能力,便于业务侧直接调用。
架构总览
下图展示一次典型上传请求从控制器到落盘与写库的调用链,以及可选的图片后处理与缩略图生成。
sequenceDiagram
participant Client as "客户端"
participant Ctrl as "控制器"
participant Facade as "Attachment 门面"
participant Service as "AttachmentService"
participant FS as "FilesystemManager/Disk"
participant DB as "dou_file 表"
participant Img as "ImageManager"
Client->>Ctrl : 提交表单/分片
Ctrl->>Facade : attachment()->store(...)
Facade->>Service : store(module, itemId, file, type, options)
Service->>FS : 解析磁盘/路径/策略
Service->>Img : 可选 resize/watermark/thumb
Service->>DB : insert/update 元数据
Service-->>Client : 返回 number/url
详细组件分析
附件服务 AttachmentService
- 单文件上传 store:
- 解析 disk、允许扩展名、大小上限、图片质量与缩略图目录
- 若业务字段已有 number 且存在对应行,走更新分支;否则分配新 number
- 校验扩展名与大小,创建目录并移动临时文件
- 可选图片缩放、水印、缩略图生成
- 计算最终体积并写入 dou_file(insert/update),记录上传者身份与时间戳
- 替换原图 replaceByNumber:
- 仅对可编辑扩展名(jpg/jpeg/png/webp)生效
- 先备份原文件,再移动或转码覆盖,必要时重建缩略图
- 仅落盘 storeToDirectory:
- 不写 dou_file,适合临时或受控目录的文件保存
- 分块上传 chunkedStore / chunkedStoreDraft:
- 委托给 ChunkedUploadHandler,支持草稿模式与拥有者身份
- 内容拉图 storeContentImages / storeFromUrl:
- 扫描富文本远程图,下载并替换为本地链接,支持草稿模式
- 草稿认领 claimByToken:
- 将草稿附件归属到真实业务主键,同时校验 module + token + 身份 + 状态,防止越权
- 清理过期草稿 cleanupUserDrafts:
- 基于 draft_expire_at 清理过期草稿及其物理文件
flowchart TD
Start(["进入 store"]) --> Parse["解析磁盘/策略/业务字段"]
Parse --> CheckExt{"扩展名允许?"}
CheckExt -- 否 --> Fail["返回错误"]
CheckExt -- 是 --> CheckSize{"大小<=上限?"}
CheckSize -- 否 --> Fail
CheckSize -- 是 --> Move["创建目录并移动文件"]
Move --> PostProc{"需要图片后处理?"}
PostProc -- 是 --> Resize["缩放/水印/缩略图"]
PostProc -- 否 --> SizeCalc["计算体积"]
Resize --> SizeCalc
SizeCalc --> WriteDB["写入 dou_file"]
WriteDB --> End(["返回 number"])
分块上传处理器 ChunkedUploadHandler
- 职责:接收分片参数(blob_num、total_blob_num、file_name、sql_link_url、file),按序合并为完整文件,写入磁盘并持久化元数据
- 草稿模式:当传入 draftCtx 时,以 status='draft' 写入,item_id=0,并携带 uploader_type/uploader_id/draft_token/draft_expire_at
- 拥有者模式:当传入 ownedCtx 时,写入 uploader_type/uploader_id 与 status='owned'
- 完成判定:当所有分片到达后,计算最终 size 并落库
sequenceDiagram
participant FE as "前端"
participant Handler as "ChunkedUploadHandler"
participant Disk as "Disk"
participant Repo as "AttachmentRepository"
FE->>Handler : 发送分片 (blob_num, total, file)
Handler->>Disk : 追加写入分片
alt 最后一个分片
Handler->>Disk : 关闭/刷新
Handler->>Repo : insert/update 元数据
Handler-->>FE : 返回进度/结果
else 中间分片
Handler-->>FE : 返回继续上传
end
文件系统与磁盘策略 FilesystemManager
- 磁盘解析顺序:upload_defaults → 命名约定推导 → disks 显式声明
- 命名约定:
- {module}_icon → images/{module}/icon/
- 标准名 [a-z0-9_]+ → images/{name}/
- 动态构建:build(relativeRoot, overrides) 用于无固定配置的视图目录
- 默认驱动:local,可扩展自定义驱动工厂 extend(driverName, factory)
配置与策略 file.php
- upload_defaults:
- upload_max_kb:单文件上传上限(KB)
- allow_extensions:允许的扩展名清单(逗号分隔,小写)
- image_quality:图片压缩质量(0-100)
- thumb_directory:缩略图相对子目录
- disks:
- local:root 指向 images/upload/
- avatar_admin:覆盖 upload_max_kb 为 100
控制器接入示例
- 前台用户控制器使用 UploadedFile 组装多文件,并通过 attachment()->store/storeDraft 进行上传或草稿暂存,支持自定义文件名、宽度限制与水印
依赖关系分析
- 附件服务依赖:
- FilesystemManager/Disk:统一磁盘访问与路径归一化
- ImageManager:图片缩放、水印、缩略图
- AttachmentRepository:dou_file 表的增删改查与草稿认领
- ChunkedUploadHandler:分块上传逻辑
- 配置依赖:
- file.php 中的 upload_defaults 与 disks 决定全局与模块级上传策略
- 门面依赖:
- Attachment 门面代理到 AttachmentService,保持调用简洁
classDiagram
class AttachmentService {
+store(...)
+replaceByNumber(...)
+storeToDirectory(...)
+chunkedStore(...)
+claimByToken(...)
+cleanupUserDrafts(...)
}
class ChunkedUploadHandler {
+handle(...)
}
class FilesystemManager {
+disk(name)
+build(root, overrides)
+extend(driver, factory)
}
class Attachment {
<<facade>>
}
Attachment --> AttachmentService : "代理"
AttachmentService --> ChunkedUploadHandler : "委托分块上传"
AttachmentService --> FilesystemManager : "获取磁盘"
性能考量
- 分块上传:通过 ChunkedUploadHandler 将大文件拆分为多个小分片,降低单次请求压力与超时风险,提升稳定性与可恢复性
- 图片后处理:按需执行 resize/watermark/thumb,避免不必要的 CPU 与 IO 开销
- 磁盘策略:利用 upload_defaults 与 disks 覆盖,针对不同模块设置合理的 upload_max_kb 与 quality,平衡质量与体积
- 缩略图目录:thumb_directory 分离缩略图与原图,便于 CDN 缓存与独立清理策略
- 懒 GC:cleanupUserDrafts 定期清理过期草稿,释放磁盘空间与数据库行数
故障排查指南
- 上传失败常见原因:
- 扩展名不在 allow_extensions 白名单内
- 文件大小超过 upload_max_kb
- 目标目录不可写或缺少权限
- move 操作失败(临时文件异常或磁盘空间不足)
- 草稿未认领:
- 检查 claimByToken 是否传入正确的 module、draft_token、identityKind、identityId
- 确认草稿未过期(draft_expire_at)
- 分块上传不完整:
- 核对 blob_num 与 total_blob_num 是否一致
- 检查 sql_link_url 是否正确传递
- 查看最后一次分片是否成功触发 insert/update
- 图片处理后问题:
- 确认图片格式支持(resize/watermark 支持的格式)
- 检查缩略图尺寸与质量配置
结论
DouPHP 的动态资源处理以 AttachmentService 为核心,结合 FilesystemManager 的统一磁盘抽象与 ChunkedUploadHandler 的大文件分片能力,提供了安全、可控、可扩展的上传与存储方案。通过 file.php 的配置集中管理上传策略,配合草稿认领与懒 GC,确保系统在高性能与安全之间取得平衡。建议在实际业务中严格遵循白名单与大小限制,合理使用分块上传与图片后处理,并建立完善的清理与监控机制。
附录
用户上传文件存储机制
- 文件类型验证:基于 allow_extensions 白名单校验,拒绝非法扩展名
- 大小限制:依据 upload_max_kb 进行限制,超出则拒绝上传
- 路径生成规则:
- 由 FilesystemManager 根据命名约定或 disks 配置确定 root
- 业务层通过 options.directory 指定子目录
- 文件名由 basename + 原始扩展名组成,必要时随机化
- 落盘与写库:move 成功后计算 size,写入 dou_file,记录上传者身份与时间戳
临时文件生命周期管理
- 会话数据:由上层框架管理,附件服务不直接持有会话
- 上传临时文件:由 PHP 运行时管理,AttachmentService 通过 UploadedFile 读取并移动到目标目录
- 缓存数据:Excel 等库内部使用内存/磁盘缓存,可通过配置切换缓存策略(如 memory、php_temp 等)
- 草稿附件:以 status='draft' 写入,携带 draft_expire_at,由 cleanupUserDrafts 定期清理
日志文件组织结构
- 访问日志、错误日志、业务日志在本仓库中未见统一的日志子系统实现;建议在业务层通过审计或日志组件记录关键事件(如登录失败、上传失败、草稿清理等)
- 建议分类:
- 访问日志:记录接口请求与响应
- 错误日志:记录异常堆栈与上下文
- 业务日志:记录关键业务流程(上传、认领、清理)
缓存系统设计
- 数据缓存:可在业务层引入 Redis/Memcache 等缓存后端,对热点数据进行缓存
- 模板缓存:模板编译产物可缓存至磁盘或内存,减少重复编译
- 查询缓存:对频繁查询的结果进行缓存,注意失效策略与一致性
- Excel 库缓存:可通过 Settings::setCacheStorageMethod 切换缓存方式,优化大文件处理性能
文件安全控制机制
- 文件类型白名单:allow_extensions 严格控制允许扩展名
- 路径遍历防护:通过 FilesystemManager 的路径归一化与 Disk 的安全访问,避免越权访问
- 权限控制:目录写入前检查 FileHelper::permission,确保目标目录可写
- 草稿认领安全:claimByToken 同时校验 module、draft_token、uploader_type、uploader_id 与 status,防止跨身份越权
大文件处理优化方案
- 分片上传:通过 ChunkedUploadHandler 实现分片合并与状态跟踪
- 断点续传:前端需记录已上传分片,服务端按 blob_num 判断是否继续
- 进度监控:服务端返回当前分片序号与总数,前端据此更新进度条
- 并发控制:限制同一文件并发分片数,避免服务器过载
存储空间管理与清理策略
- 草稿清理:cleanupUserDrafts 基于 draft_expire_at 清理过期草稿及物理文件
- 缩略图管理:缩略图与原图分离,可按目录策略清理
- 定期任务:建议定时任务扫描 dou_file 中孤立文件并清理
- 配额管理:结合 upload_max_kb 与磁盘容量监控,防止磁盘爆满