\App\Http\HelpersFileUploadHelper

文件上传与生命周期管理

职责:以静态方法封装单文件上传、危险扩展名拦截、分类扩展名白名单、 私有文件鉴权下载(回收站状态写入已归位 MaintainFileService)。

┌────────────────── 分区导航(按此顺序阅读)──────────────────┐ 分区 1 · 常量与属性 存储目录隔离 / 默认选项 / 危险扩展名 / 分类白名单 分区 2 · 类型查询(读) fileTypeLabels / fileTypes 分区 3 · 上传(写) upload 分区 4 · 查询(读) exists / missing / url 分区 5 · 下载(读) downloadResponse 分区 6 · 内部辅助 危险扩展名合并 / 用户信息解析 / 目录派生 / 落库 └────────────────────────────────────────────────────────────┘

存储策略:

  • 目录按用户不可逆派生值隔离:{user_hash}/{hash}.{ext} (user_hash = sha256(表名|用户ID|app.key) 截断,与用户密码盐脱钩,防目录名泄露盐)
  • user_id / user_type 根据登录用户所属表名自动推断
  • 上传元信息写入 td_sys_files 表

调用约定:仅由 Service 层调用,Controller 不直接使用; 入参校验失败抛出 InvalidArgumentException。

Summary

Methods
Properties
Constants
fileTypeLabels()
fileTypes()
allowedExtensions()
isAllowedExtension()
upload()
exists()
missing()
url()
downloadResponse()
No public properties found
DANGEROUS_EXTENSIONS
No protected methods found
No protected properties found
N/A
dangerousExtensions()
resolveUserInfo()
resolveUserDir()
createFileRecord()
$tableUserTypeMap
$defaults
$fileTypeMap
N/A

Constants

DANGEROUS_EXTENSIONS

DANGEROUS_EXTENSIONS = ['php', 'php3', 'php4', 'php5', 'php7', 'phtml', 'pht', 'phar', 'cgi', 'pl', 'py', 'rb', 'sh', 'bash', 'asp', 'aspx', 'jsp', 'jspx', 'exe', 'bat', 'cmd', 'com', 'scr', 'msi', 'js', 'mjs', 'vbs']

禁止落盘的危险扩展名:web 可执行/脚本类,防 RCE 面与内联执行

Properties

$tableUserTypeMap

$tableUserTypeMap : array<string,int>

Type

array — 表名 → 用户类型常量

$defaults

$defaults : array<string,mixed>

Type

array — 默认上传选项

$fileTypeMap

$fileTypeMap : array<string,string[]>

分类扩展名白名单(小写)。

与 MaintainFileRequest::ALLOWED_MIMES 保持同一允许集:Request 是 MIME 闸门(内容嗅探),此处是所选分类的扩展名闸门(双保险);新增可上传 格式须两处同步。

分类键为稳定码(值即契约,label 走 fileTypeLabels 展示映射);历史中文值 为过渡值,存量数据已迁移,判定处保留双值兼容兜底(SysFile::isImageType)。

Type

array

Methods

fileTypeLabels()

fileTypeLabels() : array

分类码 → 中文展示名(字典 file_type 驱动,字典不可用回落内置——label 展示层单源)。 字典数据页直接增删改分类与名称(与 scene 同模式,)。

Returns

array —

fileTypes()

fileTypes() : string[]

文件分类白名单(字典 file_type 驱动,字典不可用回落 $fileTypeMap keys)。 供配置强边界(file.inline_preview_types 多选选项)与 Request 校验同源使用。 注意:字典新增分类的扩展名映射需在代码 $fileTypeMap 登记(未登记分类跳过 分类扩展名校验,mimetypes/魔数/危险名单兜底链仍在——安全闭环不依赖分类清单)。

Returns

string[] —

allowedExtensions()

allowedExtensions() : array

全部允许的扩展名集合(各分类白名单并集 ∪ 配置增配 file.allowed_extensions;rename/扩展名校验共用闸门)

Returns

array —

isAllowedExtension()

isAllowedExtension(string  $ext) : bool

扩展名是否允许(重命名/上传共用闸门):必须在分类白名单内且不在危险清单。 用途:堵住「上传 txt → 改名 md/html」类绕过(md/html 不在任何分类, 预览端按扩展名选渲染器,非法扩展名可触发非预期渲染路径)。

Parameters

string $ext

Returns

bool —

upload()

upload(\Illuminate\Http\UploadedFile  $file, string  $fileType, array  $options = []) : array

上传单个文件。

存储至 {disk}/{user_hash}/{hash}.{ext},可选写入 td_sys_files 表。

Parameters

\Illuminate\Http\UploadedFile $file

客户端上传的文件实例

string $fileType

文件分类,必填,如 '头像'、'附件'

array $options

{@see \App\Http\Helpers\self::$defaults} 中任意键值对

Throws

\InvalidArgumentException

文件无效 / fileType 为空 / 存储失败

Returns

array —

exists()

exists(string  $path, string  $disk = 'public') : bool

检查物理文件是否存在。

Parameters

string $path
string $disk

Returns

bool —

missing()

missing(string  $path, string  $disk = 'public') : bool

检查物理文件是否不存在。

Parameters

string $path
string $disk

Returns

bool —

url()

url(string  $path, string  $disk = 'public') : string|null

获取公开访问 URL。

local 盘不对外暴露,始终返回 null(upload() 响应体使用)。

Parameters

string $path
string $disk

Returns

string|null —

完整 URL;不可用时返回 null

downloadResponse()

downloadResponse(string  $path, string|null  $disk = 'local', string|null  $outputName = null, bool  $inline = false) : \Symfony\Component\HttpFoundation\StreamedResponse

流式下载响应(支持 HTTP Range,206 Partial Content)。

用于 local 盘等私有文件的代理下载;Controller 中 return 此响应即可。

  • 请求带 Range 头(
  • 无 Range(普通下载)→ 200 全文 + attachment,行为与原先一致。

Parameters

string $path

存储相对路径

string|null $disk

磁盘,默认 local

string|null $outputName

浏览器下载文件名,null 取存储名

bool $inline

内联展示(图片预览/缩略图);默认附件下载

Throws

\InvalidArgumentException

文件不存在

Returns

\Symfony\Component\HttpFoundation\StreamedResponse —

dangerousExtensions()

dangerousExtensions() : string[]

生效的危险扩展名 = 内置底线 ∪ 配置增配(file.dangerous_extensions,逗号分隔小写)。 配置只能增加不能移除(内置恒在,防放宽安全底线)。

Returns

string[] —

resolveUserInfo()

resolveUserInfo() : array

根据登录用户所属表名推断 [user_id, user_type]。

Returns

array —

resolveUserDir()

resolveUserDir() : string

生成用户专属存储目录名。

取 sha256(表名|用户ID|app.key) 前 32 位:与用户密码盐脱钩的不可逆派生值, 公开 URL 中的目录名不再泄露 salt。仅影响新上传,存量路径不动。未登录返回 '0'。

Returns

string —

createFileRecord()

createFileRecord(array  $info, array  $config, int  $userId, int  $userType) : int|null

写入 td_sys_files 记录。

DB 写入失败不抛异常(文件已落盘,不回滚)。

Parameters

array $info
array $config
int $userId
int $userType

Returns

int|null —

新建记录 id(前端引用文件用);失败返回 null