2.4 KiB
2.4 KiB
7.1 文件上传全流程
:::tip 概述 上传采用两段式:先传临时目录(temp),表单提交保存时才转正到模块正式目录。未落库的临时文件不对外暴露,杜绝"传了不保存"的孤儿文件被枚举。 :::
两段式流程
① 上传接口(POST,multipart)
upload.SaveTemp → {upload_dir}/temp/{yyyymmdd}/{32位随机}.{ext}
返回访问 URL(前端表单持有)
② 保存记录(Add/Update)
ModuleSpec FileFields 声明 → saveTransform
upload.SaveFromTemp → {upload_dir}/{模块目录}/{yyyymmdd}/{时分秒_随机}.{ext}
入库的是相对 uploadDir 的路径(不含 upload 段)
③ 读侧
readTransform 补访问域名 → 完整 URL 返回前端
多文件字段走 SaveFromTempMulti(逗号分隔逐段转正)。
未转正的临时文件由每日 03:30 的清理任务(cmd/api 启动序列注册)按 file.tempExpireDays(默认 7 天)清理 temp/{日期} 目录——"传了不保存"的中间文件不会永久滞留。
物理路径契约
临时:{upload_dir}/temp/{yyyymmdd}/{32位随机}.{ext}
正式:{upload_dir}/{正式子目录}/{yyyymmdd}/{时分秒_随机}.{ext}
入库:相对 uploadDir 的路径(不含 upload 段)
访问:file.domainUrl + "/" + 入库路径(nginx 直供)
核心 API(utility/upload)
| 函数 | 说明 |
|---|---|
| SaveTemp(ctx, fileHeader, typ) | 两段式第一段:落临时目录 |
| SaveFromTemp(ctx, fileUrl, saveDir) | 第二段:转正到模块目录 |
| SaveFromTempMulti | 多文件版本 |
| GetFileUrl(ctx, path) | 相对路径 → 访问 URL |
| SafeJoin(base, rel) | 路径穿越防护(清洗后必须仍在基目录内) |
| BaseDir / TempBaseDir / GetAccessBase | 目录基址查询 |
安全设计
| 防护 | 实现 |
|---|---|
| 扩展名白名单 | imageExt / fileExt 配置(服务端校验) |
| 内容嗅探 | 图片类校验真实文件头(isImage) |
| 大小限制 | file.maxSize(应用)+ client_max_body_size(nginx)对齐 |
| 路径穿越 | SafeJoin 断言 |
| temp 枚举 | nginx 与应用开发回退均封禁 /temp/ |
| SVG 风险 | nginx 强制附件下载 + nosniff |
与 dto 的配合
文件上传字段用 *multipart.FileHeader + UnmarshalBody 逃生舱解析(见 3.3 契约层开发)。