Skip to content

云储存上传

目录


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.php

3. 功能流程图

文件上传流程

┌─────────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   选择文件   │ --> │  文件验证   │ --> │  选择存储   │ --> │  执行上传   │
└─────────────┘     └─────────────┘     └─────────────┘     └─────────────┘

                                                                   v
┌─────────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   返回URL   │ <-- │  记录附件   │ <-- │  存储文件   │ <-- │  生成路径   │
└─────────────┘     └─────────────┘     └─────────────┘     └─────────────┘

详细步骤说明

  1. 选择文件

    • 前端选择要上传的文件
    • 支持拖拽上传和点击选择
  2. 文件验证

    • 验证文件类型是否允许
    • 验证文件大小是否超限
    • 验证文件是否合法
  3. 选择存储方式

    • 根据配置选择默认存储
    • 支持指定存储方式
  4. 执行上传

    • 生成存储路径和文件名
    • 调用存储驱动上传文件
  5. 存储文件

    • 本地存储:保存到服务器磁盘
    • 云存储:上传到云服务商
  6. 记录附件

    • 保存附件信息到数据库
    • 生成访问URL
  7. 返回结果

    • 返回文件URL和附件ID

4. 数据表

附件表 (sys_attachment)

字段名类型长度是否为空默认值说明
att_idint11自增附件ID,主键
namevarchar255-附件名称
real_namevarchar255NULL原始文件名
pathvarchar500-存储路径
urlvarchar500-访问URL
att_typevarchar50-附件类型
att_sizeint110文件大小(字节)
storage_typevarchar50-存储方式
cate_idint110分组ID
create_timedatetime-CURRENT_TIMESTAMP创建时间

索引说明

  • PRIMARY KEY (att_id)
  • KEY idx_cate_id (cate_id)
  • KEY idx_type (att_type)

附件分组表 (sys_attachment_category)

字段名类型长度是否为空默认值说明
idint11自增分组ID,主键
namevarchar50-分组名称
pidint110父分组ID
sortint110排序
create_timedatetime-CURRENT_TIMESTAMP创建时间

索引说明

  • PRIMARY KEY (id)
  • KEY idx_pid (pid)

5. 代码说明

5.1 接口定义

图片上传接口

项目说明
接口地址/sys/image
请求方式POST
Content-Typemultipart/form-data

请求参数:

参数名类型是否必填说明
filefile图片文件
cate_idint分组ID

响应示例:

json
{
    "code": 200,
    "message": "上传成功",
    "data": {
        "url": "https://xxx.com/upload/image/202401/01/xxx.jpg",
        "att_id": 10001
    }
}

5.2 核心代码

上传服务

php
/**
 * 图片上传
 * @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);
}

核心上传处理

php
/**
 * 上传后处理
 * @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 配置说明

php
// 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: 上传失败,提示"文件类型不允许"

问题现象: 上传文件时返回错误,提示文件类型不被允许。

原因分析:

  1. 文件扩展名不在允许列表中
  2. 文件MIME类型检测失败
  3. 文件头信息不完整

解决方案:

  1. 检查文件扩展名是否在配置允许列表
  2. 确认文件未损坏且格式正确
  3. 检查上传配置中的文件类型限制

预防措施:

  • 前端提前校验文件类型
  • 提供清晰的文件类型提示
  • 定期更新允许的文件类型列表

Q2: 大文件上传超时

问题现象: 上传大文件时,请求超时或上传失败。

原因分析:

  1. PHP上传大小限制
  2. 请求超时时间设置过短
  3. 服务器内存不足

解决方案:

  1. 修改php.ini配置:
    ini
    upload_max_filesize = 100M
    post_max_size = 100M
    max_execution_time = 300
  2. 使用分片上传功能
  3. 增加服务器内存限制

预防措施:

  • 前端显示上传进度
  • 大文件自动启用分片上传
  • 合理设置文件大小限制

Q3: 云存储配置后无法上传

问题现象: 配置云存储后,上传文件报错或失败。

原因分析:

  1. 云存储密钥配置错误
  2. 存储桶权限设置不正确
  3. 网络连接问题

解决方案:

  1. 检查AccessKey和SecretKey是否正确
  2. 确认存储桶读写权限已开启
  3. 测试与云服务商的网络连通性
  4. 查看云服务商控制台是否有相关错误日志

预防措施:

  • 配置后提供测试连接功能
  • 定期检查存储配置有效性
  • 设置配置错误告警

Q4: 附件删除后文件仍然存在

问题现象: 删除附件记录后,存储中的文件没有被删除。

原因分析:

  1. 删除逻辑只删除了数据库记录
  2. 文件权限不足无法删除
  3. 文件被其他记录引用

解决方案:

  1. 检查删除逻辑是否包含文件删除操作
  2. 确认服务器对存储目录有删除权限
  3. 实现文件引用计数,无引用时才删除

预防措施:

  • 定期清理无效文件
  • 实现文件引用计数机制
  • 提供手动清理工具

修订记录

版本日期修订人修订内容
v1.02024-01-01-初始版本
v2.02024-04-02-按照开发手册规范重构文档结构