Files
rxthinkcmf-wiki/RXThinkCMF_Laravel13_EleVue/zh/tools/upload-config.md
T
2026-10-08 17:50:29 +08:00

10 KiB
Raw Blame History

7.2 上传配置详解

:::tip 概述 文件上传配置定义在 config/file.php 和 .env 中,控制上传目录、访问域名、大小限制、允许的文件类型等。所有值通过 env() 函数从 .env 读取,支持多环境切换;config/file.php 中的注释明确标注了每个配置的读取方。 :::

配置文件

// config/file.php — 文件上传配置(来自项目实际代码,节选)
return [
    // 文件上传根目录(相对 public),所有上传文件均存放于此
    // 读取方:UploadController::uploadFile()、save_file()、save_remote_file()
    'upload_dir' => env('FILE_UPLOAD_DIR', 'uploads'),

    // 文件访问域名,用于拼接完整 URL;为空时返回相对路径
    // 读取方:get_file_url()、save_content()、processContentFieldsOnRead()
    'domain_url' => env('FILE_DOMAIN_URL', ''),

    // 临时文件目录名(相对 upload_dir)
    // 读取方:UploadController::uploadFile()(上传落盘目录)、save_file()(识别 temp/ 开头的临时文件后迁移到正式目录)
    'temp_dir' => 'temp',

    // 允许上传的图片后缀(逗号分隔)
    // 安全说明:svg 已移除,因其可内嵌 JavaScript 导致 XSS 风险
    'image_ext' => env('FILE_IMAGE_EXT', 'gif,jpg,jpeg,png,bmp,webp'),

    // 允许上传的文件后缀(逗号分隔)
    'file_ext' => env('FILE_EXT', 'gif,jpg,jpeg,png,bmp,webp,doc,docx,xls,xlsx,ppt,pptx,pdf,zip,rar,txt,csv'),

    // 单文件最大尺寸(MB),转换为字节存储
    'max_size' => (int) env('FILE_MAX_SIZE', 10) * 1024 * 1024,
];

配置项详解

配置项 .env 变量 默认值 说明
upload_dir FILE_UPLOAD_DIR uploads 上传根目录(相对 public/),所有文件存储于此
domain_url FILE_DOMAIN_URL (空) 文件访问域名,用于拼接完整 URL
temp_dir — temp 临时目录名(相对 upload_dir),上传接口的落盘目录,save_file() 据此识别待迁移文件
image_ext FILE_IMAGE_EXT gif,jpg,jpeg,png,bmp,webp 图片允许后缀(无 svg)
file_ext FILE_EXT 图片+文档+压缩包等 文件允许后缀
max_size FILE_MAX_SIZE 10 单文件最大大小(MB,配置内自动转字节)

upload_dir — 上传根目录

# .env
FILE_UPLOAD_DIR=uploads

要求:

  • 本项目为相对 public/ 的目录,Web 可直接访问其中的静态文件
  • 目录必须存在且有写入权限(Docker 镜像已创建并授权 public/uploads)
  • 上传文件随项目目录持久化(Docker 下即宿主机目录,不依赖命名卷)

各环境示例:

环境 值 实际位置
本地开发 uploads 项目/public/uploads/
Linux 生产 uploads(或绝对路径) Nginx root 下可直接访问
Docker uploads 挂载的项目目录内

domain_url — 文件访问域名

# .env
FILE_DOMAIN_URL=https://cdn.example.com

要求:

  • 不要以 / 结尾(读取方会 rtrim 处理,但配置时保持规范)
  • 必须是前端可访问的域名
  • 本地开发可留空(接口返回相对路径,由同源/Vite 代理直接访问)

各环境示例:

环境 域名示例
本地开发 (留空,使用相对路径)
虚拟域名开发 http://file.laravel13.elevue
测试环境 https://test-cdn.example.com
生产环境 https://cdn.example.com 或对象存储域名

temp_dir — 临时目录

// config/file.php
'temp_dir' => 'temp',

说明:

  • 上传接口 uploadFile 把文件写入 {upload_dir}/temp/Ymd/,不直接落正式目录
  • save_file() / save_content() 依据 temp/ 前缀把文件迁移到 Service 的 fileSaveDir 子目录
  • 正式目录未被表单引用的临时文件可按周期清理(见下方「问题 4」)

max_size — 文件大小限制

# .env(单位 MB,config 内 ×1024×1024 转字节)
FILE_MAX_SIZE=10

建议值:

场景 建议值 说明
头像上传 2 MB 图片通常不会太大
文章封面 5 MB 压缩后的图片
通用文件上传 10 MB 默认值
文档上传 20 MB PDF/Word 等

:::warning 双层限制 业务层限制生效前,PHP 层必须放行:php.ini 的 upload_max_filesize / post_max_size 需 大于等于 FILE_MAX_SIZE(建议 50M)。 :::

image_ext / file_ext — 后缀白名单

# .env
FILE_IMAGE_EXT=gif,jpg,jpeg,png,bmp,webp      # uploadFile?type=image 校验
FILE_EXT=gif,jpg,jpeg,png,bmp,webp,doc,docx,xls,xlsx,ppt,pptx,pdf,zip,rar,txt,csv   # uploadFile(默认 type=file)校验

说明:

  • image_ext 用于 type=image,只允许图片格式;svg 已移除(内嵌 JS 的 XSS 风险)
  • file_ext 用于 type=file,允许图片、文档、压缩包等常见格式
  • 白名单为空数组时跳过校验(防御性兜底),生产环境务必保留非空配置

.env 配置示例

# 开发环境
FILE_UPLOAD_DIR=uploads
FILE_DOMAIN_URL=
FILE_MAX_SIZE=10

# 生产环境(示例)
FILE_UPLOAD_DIR=uploads
FILE_DOMAIN_URL=https://cdn.example.com
FILE_MAX_SIZE=10
FILE_IMAGE_EXT=gif,jpg,jpeg,png,bmp,webp
FILE_EXT=gif,jpg,jpeg,png,bmp,webp,doc,docx,xls,xlsx,pdf,zip,rar

目录结构

public/
└── {upload_dir}/                        # upload_dir(默认 uploads)
    ├── temp/                            # 临时目录(UploadController 上传落盘)
    │   └── 20261002/
    │       └── 123456_a1b2c3d4e5f6.jpg
    ├── article/                         # 正式目录(由 Service 的 fileSaveDir 配置)
    │   └── 20261002/
    │       └── 153052_9f8e7d6c5b4a.pdf
    ├── example/
    ├── user/
    │   └── avatar/
    └── file_template/

目录命名规则

目录 说明 来源
temp/ 临时文件目录(上传接口落盘) config/file.php → temp_dir
{module}/ 按模块的正式目录 Service 的 fileSaveDir(未配置按模型名推导)
{Ymd}/ 日期子目录 自动创建
{His}_{random}.{ext} 时间+随机文件名 防冲突与可预测性

文件生命周期

1. 前端上传 → POST /api/upload/uploadFile
   保存到: public/uploads/temp/20261002/123456_a1b2c3d4e5f6.jpg
   返回: filePath + fileUrl

2. 前端提交表单 → 携带 filePath
   POST /api/example/add { name: "测试", avatar: "temp/20261002/..." }

3. ExampleService::add() → processFileFieldsOnSave → save_file()
   temp/ → 迁移到 fileSaveDir(example/)后,正式目录相对路径入库
   (其它分支:去掉域名直接用;外部 URL → 下载保存)

4. ExampleService::detail() → processFileFieldsOnRead → get_file_url()
   相对路径 → 拼接 FILE_DOMAIN_URL
   example/20261002/...
   → https://cdn.example.com/example/20261002/...

与 Service 层的配合

class ArticleService extends BaseTenantService
{
    use HasFileProcessing, /* 其他 Trait */;

    protected array $fileFields = ['cover'];
    protected string $fileSaveDir = 'article';
}

实际路径映射:

配置:
  FILE_UPLOAD_DIR = uploads
  FILE_DOMAIN_URL  = https://cdn.example.com
  fileSaveDir      = article

结果:
  上传文件:     public/uploads/temp/20261002/xxx.jpg
  Service 归一化后入库: article/20261002/xxx.jpg
  前端显示:     https://cdn.example.com/article/20261002/xxx.jpg

Nginx 配置

静态文件直接由 Nginx 返回

# 项目方案:public/uploads 位于站点根内,location / 由 try_files 直接返回静态文件
location / {
    try_files $uri $uri/ /index.php?$query_string;
}

禁止上传目录执行 PHP

# docker/nginx.conf 已内置:仅入口 index.php 可执行,其余 .php 一律 404
location ~ \.php$ {
    return 404;
}

禁止访问隐藏文件

location ~ /\. {
    deny all;
}

常见问题

问题 1:上传提示"目录不可写"

原因: public/uploads 没有写入权限。

解决:

# Linux
chmod -R 755 public/uploads
chown -R www-data:www-data public/uploads

# Docker:镜像内已 chown(storage / bootstrap/cache / public/uploads)

问题 2:上传后图片不显示

原因: FILE_DOMAIN_URL 配置错误或与前端不同源。

解决:

  1. 检查 .env 中 FILE_DOMAIN_URL 是否为前端可访问的地址(改后 php artisan config:clear)
  2. 留空时用相对路径——确认前端与后端同源或经代理
  3. 浏览器直接打开图片 URL 验证

问题 3:文件大小限制不生效

原因: PHP 层限制小于业务层。

解决:

; php.ini(修改后重启服务)
upload_max_filesize = 50M
post_max_size = 50M
max_execution_time = 300

问题 4:临时文件未清理

原因: 上传后未提交表单(或表单未引用)的文件停留在 temp/,不会被迁移。

解决: 定期清理 temp/ 中超过 1 天的文件:

# 定时任务:每天凌晨清理 1 天前的临时文件
find public/uploads/temp/ -type f -mtime +1 -delete

安全建议

建议 说明
扩展名白名单 只允许已知安全的文件类型(FILE_IMAGE_EXT/FILE_EXT)
移除高危后缀 svg 已移除;生产白名单去掉 sql/js/css 等可执行类
MIME 类型检查 导入等场景配合 finfo 检测真实 MIME(见 7.4 数据导入导出)
随机文件名 服务端生成(时间+随机串),防止路径遍历和文件名注入
文件大小限制 FILE_MAX_SIZE 防止大文件 DoS
Nginx 禁止执行 PHP 仅入口 index.php 可执行(Docker 配置已内置)
定期清理临时文件 temp/ 内未被引用的文件按周期清理,避免磁盘空间被占满