目录
- 功能说明
- 设计思路
- 功能流程图
- 数据表
- 代码说明
- 常见问题
1. 功能说明
概述
云储存上传模块负责系统中所有文件的上传和管理功能,支持多种存储方式,提供统一的文件上传接口和管理能力。
适用场景
- 用户头像上传
- 商品图片上传
- 视频文件存储
- 文档文件管理
- 富文本编辑器图片上传
功能特点
- 多存储方式支持:本地存储、阿里云OSS、腾讯云COS、七牛云存储
- 文件类型支持:图片、视频、音频、文档等多种类型
- 附件分组管理:支持创建分组、移动附件
- 存储配置灵活:可配置默认存储方式
2. 设计思路
设计理念
采用"统一接口、多存储适配"的设计理念,通过抽象上传接口,实现不同存储方式的无缝切换。
技术选型
架构设计
系统采用分层架构设计:
- 接口层:Controller接收上传请求
- 服务层:Service处理业务逻辑
- 核心层:CoreUpload处理上传逻辑
- 存储层:不同存储方式的具体实现
- 数据层:附件信息持久化
模块划分
upload/ ├── controller/ # 控制器层 │ ├── Upload.php # 上传控制器 │ └── Sys.php # 系统配置控制器 ├── service/ # 业务逻辑层 │ ├── UploadService.php │ └── CoreUploadService.php ├── core/ # 核心上传层 │ ├── BaseUpload.php │ ├── Local.php │ ├── Aliyun.php │ ├── Tencent.php │ ├── Qiniu.php │ └── UploadLoader.php └── model/ # 数据模型层 ├── Attachment.php └── AttachmentCategory.php3. 功能流程图
文件上传流程
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 选择文件 │ --> │ 文件验证 │ --> │ 选择存储 │ --> │ 执行上传 │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ v ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 返回URL │ <-- │ 记录附件 │ <-- │ 存储文件 │ <-- │ 生成路径 │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘详细步骤说明
选择文件
- 前端选择要上传的文件
- 支持拖拽上传和点击选择
文件验证
- 验证文件类型是否允许
- 验证文件大小是否超限
- 验证文件是否合法
选择存储方式
- 根据配置选择默认存储
- 支持指定存储方式
执行上传
- 生成存储路径和文件名
- 调用存储驱动上传文件
存储文件
- 本地存储:保存到服务器磁盘
- 云存储:上传到云服务商
记录附件
- 保存附件信息到数据库
- 生成访问URL
返回结果
- 返回文件URL和附件ID
4. 数据表
附件表 (sys_attachment)
索引说明
- PRIMARY KEY(
att_id) - KEY
idx_cate_id(cate_id) - KEY
idx_type(att_type)
附件分组表 (sys_attachment_category)
| 字段名 | 类型 | 长度 | 是否为空 | 默认值 | 说明 |
|---|---|---|---|---|---|
| id | int | 11 | 否 | 自增 | 分组ID,主键 |
| name | varchar | 50 | 否 | - | 分组名称 |
| pid | int | 11 | 否 | 0 | 父分组ID |
| sort | int | 11 | 否 | 0 | 排序 |
| create_time | datetime | - | 否 | CURRENT_TIMESTAMP | 创建时间 |
索引说明
- PRIMARY KEY(
id) - KEY
idx_pid(pid)
5. 代码说明
5.1 接口定义
图片上传接口
| 项目 | 说明 |
|---|---|
| 接口地址 | /sys/image |
| 请求方式 | POST |
| Content-Type | multipart/form-data |
请求参数:
| 参数名 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| file | file | 是 | 图片文件 |
| cate_id | int | 否 | 分组ID |
响应示例:
{ "code": 200, "message": "上传成功", "data": { "url": "https://xxx.com/upload/image/202401/01/xxx.jpg", "att_id": 10001 } }5.2 核心代码
上传服务
/** * 图片上传 * @param $file 上传的文件 * @param int $cate_id 分组ID * @param bool $is_attachment 是否记录附件 * @return array */ public function image($file, int $cate_id = 0, $is_attachment = true) { $dir = $this->root_path . '/' . 'image' . '/' . date('Ym') . '/' . date('d'); $core_upload_service = new CoreUploadService($is_attachment); return $core_upload_service->image($file, $dir, $cate_id); }核心上传处理
/** * 上传后处理 * @param string $file_dir 文件目录 * @param string $type 文件类型 * @param int $cate_id 分组ID * @return array */ public function after(string $file_dir, string $type, int $cate_id = 0) { $file_info = $this->upload_driver->getFileInfo(); $dir = $this->root_path . '/' . $file_dir; // 执行上传 $this->upload_driver->setType($type)->setValidate($this->validate)->upload($dir); $file_name = $this->upload_driver->getFileName(); $full_path = $this->upload_driver->getFullPath($dir); $url = $this->upload_driver->getUrl($full_path); if ($this->is_attachment) { // 记录附件信息 $data = [ 'name' => $file_name, 'dir' => $dir, 'att_type' => $type, 'real_name' => $file_info['name'], 'att_size' => $file_info['size'], 'storage_type' => self::$storage_type, 'path' => $full_path, 'url' => $url, 'cate_id' => $cate_id, ]; $att_id = (new CoreAttachmentService())->add($data); } return [ 'url' => $url, 'att_id' => $att_id ?? 0 ]; }5.3 配置说明
// config/storage.php return [ // 默认存储方式 'default' => 'local', // 本地存储配置 'local' => [ 'driver' => 'local', 'root' => root_path() . 'public/upload', 'url' => '/upload', ], // 阿里云OSS配置 'aliyun' => [ 'driver' => 'aliyun', 'access_key' => env('ALIYUN_ACCESS_KEY'), 'secret_key' => env('ALIYUN_SECRET_KEY'), 'bucket' => env('ALIYUN_BUCKET'), 'endpoint' => env('ALIYUN_ENDPOINT'), ], // 腾讯云COS配置 'tencent' => [ 'driver' => 'tencent', 'secret_id' => env('TENCENT_SECRET_ID'), 'secret_key' => env('TENCENT_SECRET_KEY'), 'bucket' => env('TENCENT_BUCKET'), 'region' => env('TENCENT_REGION'), ], ];6. 常见问题
Q1: 上传失败,提示"文件类型不允许"
问题现象:上传文件时返回错误,提示文件类型不被允许。
原因分析:
- 文件扩展名不在允许列表中
- 文件MIME类型检测失败
- 文件头信息不完整
解决方案:
- 检查文件扩展名是否在配置允许列表
- 确认文件未损坏且格式正确
- 检查上传配置中的文件类型限制
预防措施:
- 前端提前校验文件类型
- 提供清晰的文件类型提示
- 定期更新允许的文件类型列表
Q2: 大文件上传超时
问题现象:上传大文件时,请求超时或上传失败。
原因分析:
- PHP上传大小限制
- 请求超时时间设置过短
- 服务器内存不足
解决方案:
- 修改php.ini配置:ini
upload_max_filesize = 100M post_max_size = 100M max_execution_time = 300 - 使用分片上传功能
- 增加服务器内存限制
预防措施:
- 前端显示上传进度
- 大文件自动启用分片上传
- 合理设置文件大小限制
Q3: 云存储配置后无法上传
问题现象:配置云存储后,上传文件报错或失败。
原因分析:
- 云存储密钥配置错误
- 存储桶权限设置不正确
- 网络连接问题
解决方案:
- 检查AccessKey和SecretKey是否正确
- 确认存储桶读写权限已开启
- 测试与云服务商的网络连通性
- 查看云服务商控制台是否有相关错误日志
预防措施:
- 配置后提供测试连接功能
- 定期检查存储配置有效性
- 设置配置错误告警
Q4: 附件删除后文件仍然存在
问题现象:删除附件记录后,存储中的文件没有被删除。
原因分析:
- 删除逻辑只删除了数据库记录
- 文件权限不足无法删除
- 文件被其他记录引用
解决方案:
- 检查删除逻辑是否包含文件删除操作
- 确认服务器对存储目录有删除权限
- 实现文件引用计数,无引用时才删除
预防措施:
- 定期清理无效文件
- 实现文件引用计数机制
- 提供手动清理工具
修订记录
| 版本 | 日期 | 修订人 | 修订内容 |
|---|---|---|---|
| v1.0 | 2024-01-01 | - | 初始版本 |
| v2.0 | 2024-04-02 | - | 按照开发手册规范重构文档结构 |