Files
easygoadmin-wiki/EasyGoAdmin_GoFrame_AntdVue/zh/tools/upload.md
T
2026-10-20 21:53:26 +08:00

103 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 7.1 文件上传全流程
:::tip 概述
上传采用两段式:SaveTemp 先入临时目录(白名单+大小+图片嗅探校验),表单保存时 SaveFromTemp 移动到正式子目录。未走第二段的 temp 文件由每日 03:30 清理任务过期回收,不污染正式目录。
:::
## 上传接口
```text
POST /api/upload/uploadFile (sys:upload:file)multipart 字段名 file
POST /api/upload/uploadFiles (sys:upload:files)多文件
入参 type=image 时按图片规则校验,否则按普通文件
```
multipart 字段约定:`*ghttp.UploadFile` + `p:"file"` 标签。
## 两段式流程图
```text
阶段一(上传时,POST /api/upload/uploadFile):
浏览器 ──multipart──▶ SaveTemp
│ ① 大小 ≤ file.maxSize(MB)
│ ② 扩展名 ∈ file.fileExt / file.imageExt
│ ③ type=image:前512B 魔数嗅探须 image/*
▼
落盘 {uploadDir}/temp/{yyyymmdd}/{32位hex}.{ext}
▼
返回 {url:"{domainUrl}/temp/...", path:"temp/.../x.png"(入库形态)} → 前端预览
阶段二(表单保存时,spec.saveTransform / SaveFileField):
表单携带 temp URL ──▶ SaveFromTemp(url, saveDir="user")
│ 剥离 domainUrl → 相对路径
│ 以 temp/ 开头?──否──▶ 已正式/外站:归一化或原样返回
│ 是
▼
{uploadDir}/{saveDir}/{yyyymmdd}/{HHmmss}_{4字节hex}.{ext}(Rename,跨卷复制回退)
▼
库中写入相对路径 "user/20261017/153012_ab12.png"
▼
读侧 GetFileUrl:domainUrl + "/" + 相对路径 ──▶ 前端展示
```
## SaveTemp 校验链(第一段)
```text
1) 大小 ≤ file.maxSize(MB,默认 10)
2) 扩展名白名单:file.fileExt(普通)/ file.imageExt(图片)
3) 读取前 512 字节:type=image 时做魔数嗅探(http.DetectContentType 须 image/*),并据文件头嗅探 MIME
落盘:{file.uploadDir}/{file.tempDir}/{yyyymmdd}/{32位随机hex}.{ext}
返回 Result{Url(可预览完整地址), Path(相对 uploadDir,入库形态), Name, Size, Ext, Mime}
```
## SaveFromTemp 处理规则(第二段)
| 入参形态 | 处理 |
|---------|------|
| 空值 | 返回 "" |
| 本站 URL(匹配 file.domainUrl 前缀) | 剥离域名得相对路径 |
| 相对路径以 temp/ 开头 | 移动到 {saveDir}/{yyyymmdd}/{HHmmss}_{4字节hex}.{ext}(os.Rename,跨卷回退"复制后删源");返回新相对路径(入库形态) |
| 已是正式相对路径 | 归一化后原样返回(幂等) |
| 外站 http(s) URL | 原样返回(不处理外链) |
SaveFromTempMulti:按逗号拆分逐个转正(如 article.images 多图字段)。
## 路径契约
```text
数据库统一存"相对 uploadDir 的路径"(如 tenant/20260923/x.png)
对外访问地址 = GetFileUrl = file.domainUrl + "/" + 相对路径
(domainUrl 为空时用 /upload/ 相对路径)
静态访问:router SetServerRoot("resource") 提供 /upload/**
Response 中间件对 .svg 强制 attachment + nosniff(防存储型 XSS)
```
## 声明式用法(ModuleSpec)
```go
FileFields: []string{"avatar"} + FileSaveDir: "user"
// 写侧 saveTransform 自动 temp→正式;读侧 readTransform 自动 GetFileUrl 补域名
// MultiFileFields 处理逗号多文件;ContentFields 走富文本流程
```
模块已接入:user(avatar)、article(cover/images/content)、tenant(logo)、example(avatar) 等。
## 临时目录清理(运营闭环)
```text
cmd.go 注册 gcron "0 30 3 * * *"(每日 03:30)→ logic.CleanTemp:
扫描 {uploadDir}/{tempDir} 下目录名为 yyyymmdd 且早于
now-file.tempExpireDays(默认 7 天,0=不清理)的日期目录整目录删除
返回删除数量并记日志
```
## 常见问题
| 症状 | 排查 |
|------|------|
| 上传图片 404 | file.domainUrl 与静态服务根的相对关系;库中存相对路径,展示层必须经 GetFileUrl |
| 上传被拒 | 扩展名不在白名单 / 超 maxSize / 图片魔数嗅探不过(改后缀伪造) |
| temp 文件堆积 | CleanTemp 的 gcron 已注册?file.tempExpireDays > 0? |
| 跨卷移动失败 | os.Rename 失败自动回退"复制后删源";检查磁盘空间与权限 |
| 富文本图片不显示 | 读取侧走 Detail/List(基座自动 Expand),不要直接 Struct 输出 |