# 8.4 文件上传接口 :::tip 概述 上传接口统一 multipart:POST /api/upload/uploadFile(单文件)与 uploadFiles(多文件),字段名 file。返回可预览 URL 与入库形态 Path。两段式协议详见 [7.1 文件上传全流程](../tools/upload)。 ::: ## 接口定义 ```text POST /api/upload/uploadFile 权限 sys:upload:file multipart 字段名 file POST /api/upload/uploadFiles 权限 sys:upload:files 多文件 入参 type=image 时按图片规则校验(扩展名 + 魔数嗅探),否则按普通文件 ``` multipart 标签约定:`*ghttp.UploadFile` + `p:"file"`。 ## 请求示例 ```sh curl -X POST http://127.0.0.1:8000/api/upload/uploadFile \ -H "Authorization: Bearer " \ -F "file=@avatar.png" \ -F "type=image" ``` ## 响应结构 ```json { "code": 0, "ok": true, "msg": "操作成功", "data": { "originalName": "avatar.png", "fileExtension": "png", "fileType": "image/png", "fileSize": 102400, "fileName": "a1b2c3....png", "filePath": "temp/20261018/a1b2c3....png", "fileUrl": "http://file.example.com/temp/20261018/a1b2c3....png" } } ``` | 字段 | 用途 | |------|------| | originalName | 原始文件名(前端展示/回显用) | | fileExtension | 扩展名(小写,无点) | | fileType | MIME 类型(按文件头内容嗅探,识别不出为 application/octet-stream) | | fileSize | 文件大小(字节) | | fileName | 存储文件名(= filePath 的 basename,32位随机.ext) | | filePath | 相对 uploadDir 路径(**入库形态**;表单保存时转正) | | fileUrl | 可直接预览的完整地址(domainUrl 拼接) | ## 校验失败场景 | 场景 | 响应 | |------|------| | 超过 maxSize(默认 10MB) | code=1,msg 提示大小超限 | | 扩展名不在白名单 | code=1,msg 提示类型不允许 | | type=image 但魔数非 image/* | code=1,拒绝(防改后缀伪造) | | 未登录/令牌无效 | code=401 | | 无权限 | code=403 | | 触发限流 | code=429 | ## 两段式协议(对接要点) ```text 上传阶段:只落 temp 目录(upload/temp/日期/随机名) 前端持有 url;表单字段存 url 或 path 保存阶段:业务接口(userAdd/articleAdd/...)的 spec.saveTransform 自动 SaveFromTemp 转正到业务目录(user/日期/时间戳_随机.ext) 库存相对路径 读取阶段:Detail/List 的 readTransform 经 GetFileUrl 补域名 ``` 前端不要在上传后直接把 url 当最终地址持久化到业务表——转正由业务保存触发。 ## 批量上传(uploadFiles) ```text POST /api/upload/uploadFiles multipart 多个 file 字段 → 逐个 SaveTemp → 返回结果数组 (每项同单文件结构;单项校验失败不影响其他项——对照导入的逐行语义) ``` ## 安全链速查 ```text 大小限制 → 扩展名白名单 → 图片魔数嗅探 → 随机文件名 → 两段式 → svg 附件化+nosniff → temp 定期清理(tempExpireDays + 03:30 gcron) ``` 公网开放上传时收紧 fileExt(当前含 js/css/sql)——配置见 [7.2 上传配置详解](../tools/upload-config)。