# 8.4 文件上传接口 :::tip 概述 文件上传接口将文件保存到临时目录,返回文件 URL。前端拿到 URL 后提交表单时传给后端,BaseLogic 在 add/update 时自动迁移到正式目录。通过 `type` 参数可区分上传类型(文件/图片)。 ::: ## 接口信息 | 项目 | 说明 | |------|------| | URL | `POST /api/upload/uploadFile` | | Content-Type | `multipart/form-data` | | 权限 | 无需认证(公开接口) | ## 请求参数 | 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | `file` | File | 是 | - | 文件二进制数据 | | `type` | string | 否 | `file` | 上传类型:`file`(所有合法文件)/ `image`(仅图片) | ### 成功响应 ```json { "code": 0, "ok": true, "msg": "上传成功", "data": { "fileUrl": "https://cdn.example.com/temp/20260920/a1b2c3d4e5f6.jpg", "filePath": "temp/20260920/a1b2c3d4e5f6.jpg", "originalName": "photo.jpg", "fileSize": 102400, "fileExtension": "jpg", "fileType": "image/jpeg", "fileName": "a1b2c3d4e5f6.jpg" } } ``` ### 响应字段 | 字段 | 类型 | 说明 | 示例 | |------|------|------|------| | `fileUrl` | string | 完整 URL(含域名) | `https://cdn.example.com/temp/...` | | `filePath` | string | 相对路径 | `temp/20260920/a1b2c3d4e5f6.jpg` | | `originalName` | string | 原始文件名 | `photo.jpg` | | `fileSize` | number | 文件大小(字节) | `102400` | | `fileExtension` | string | 扩展名 | `jpg` | | `fileType` | string | MIME 类型 | `image/jpeg` | | `fileName` | string | 服务器文件名(随机) | `a1b2c3d4e5f6.jpg` | ### type 参数说明 | type 值 | 说明 | 允许的后缀 | 适用场景 | |---------|------|-----------|---------| | `file`(默认) | 所有合法文件 | 由 `config/file.php` 的 `file_ext` 控制 | 文档、压缩包等 | | `image` | 仅图片 | 由 `config/file.php` 的 `image_ext` 控制 | 头像、封面等 | **file_ext 默认值:** ```text gif, jpg, jpeg, png, bmp, webp, svg, doc, docx, xls, xlsx, ppt, pptx, pdf, zip, rar, txt, csv, mp3, mp4, sql, js, css ``` **image_ext 默认值:** ```text gif, jpg, jpeg, png, bmp, webp, svg ``` ## 失败响应 ```json { "code": 1, "ok": false, "msg": "不允许的文件类型: .exe", "data": null } ``` ## 常见错误 | 错误信息 | 原因 | 解决方案 | |---------|------|----------| | `未接收到上传文件` | 表单字段名不是 `file` | 检查 FormData 的 key 是否为 `file` | | `不允许的文件类型: .xxx` | 后缀不在白名单 | 检查 `config/file.php` 的 `image_ext`/`file_ext` | | `文件大小超过限制` | 超过 `max_size` | 调整 `.env` 中 `FILE.MAX_SIZE` 配置 | | `图片文件内容无效` | 图片损坏或非图片伪装 | 重新选择文件 | | `文件上传失败` | PHP 上传错误 | 检查 `php.ini` 的 `upload_max_filesize` | ## 前端使用 ### Element Plus Upload 组件 ```vue ``` ### 手动上传 ```typescript // 上传文件 const formData = new FormData(); formData.append('file', file); const res = await http.request({ url: '/upload/uploadFile', method: 'POST', data: formData, headers: { 'Content-Type': 'multipart/form-data' }, }); // res.fileUrl = "https://cdn.example.com/temp/20260920/abc.jpg" // 上传图片(指定 type=image) const res2 = await http.request({ url: '/upload/uploadFile?type=image', method: 'POST', data: formData, headers: { 'Content-Type': 'multipart/form-data' }, }); ``` ### 提交表单时传 URL ```typescript // 前端上传后拿到 fileUrl,提交表单时传给后端 await exampleAdd({ name: '测试', avatar: 'https://cdn.example.com/temp/20260920/abc.jpg', }); // 后端 BaseLogic::add() 自动: // 1. 检测到 avatar 是临时文件 URL // 2. 调用 save_file() 迁移到正式目录 // 3. 去掉域名,只存相对路径入库 ``` ### 头像上传示例 ```vue ``` ## 上传后处理流程 ```text 前端上传 → temp/日期/xxx.jpg(临时目录) │ ▼ 提交表单(携带 URL) BaseLogic::add() │ ├─ processFileFieldsOnSave() │ └─ save_file() → 正式目录/日期/xxx.jpg │ └─ 数据库存储相对路径 │ ▼ 读取时 processFileFieldsOnRead() └─ get_file_url() → 拼接域名返回完整 URL ``` ## 配置项 ```ini ; .env [FILE] UPLOAD_DIR = D:\xampp8.2\htdocs\RXThinkCMF_ThinkPhp8_EleVue\uploads ; 上传根目录 DOMAIN_URL = https://cdn.example.com ; 文件访问域名 MAX_SIZE = 10 ; 单文件最大 10MB IMAGE_EXT = jpg,jpeg,png,gif,bmp,webp,svg FILE_EXT = jpg,jpeg,png,gif,bmp,webp,svg,doc,docx,xls,xlsx,ppt,pptx,pdf,zip,rar,txt,csv,mp3,mp4,sql,js,css ``` ## 安全设计 | 安全措施 | 说明 | |---------|------| | 文件类型校验 | 同时检查后缀名和 MIME 类型(finfo),防止伪造扩展名 | | 文件大小限制 | 通过 `config/file.php` 的 `max_size` 配置 | | 图片内容校验 | 图片类型额外用 `getimagesize` 验证,防止非图片伪装 | | 随机文件名 | 使用 `bin2hex(random_bytes(16))` 防止文件名冲突和路径遍历 | | 临时目录隔离 | 上传文件先存入 `temp/` 目录,提交表单后才迁移到正式目录 | | Nginx 防护 | 上传目录禁止执行 PHP 文件 | ## 常见问题 ### 问题 1:上传返回 401 **原因:** 上传接口需要认证,但请求头未携带 Token。 **解决:** 确保请求头包含 `Authorization: Bearer `。 ### 问题 2:上传返回"不允许的文件类型" **原因:** 文件扩展名不在白名单中。 **解决:** 检查 `.env` 中 `FILE.FILE_EXT` 或 `FILE.IMAGE_EXT` 配置。 ### 问题 3:上传后图片不显示 **原因:** `DOMAIN_URL` 配置错误或 Nginx 未配置静态文件处理。 **解决:** 检查 `.env` 中 `FILE.DOMAIN_URL` 是否正确,Nginx 是否配置了 `/uploads/` 的 alias。 ### 问题 4:大文件上传失败 **原因:** PHP 或 Nginx 限制了上传大小。 **解决:** ```ini ; php.ini upload_max_filesize = 20M post_max_size = 25M ; .env FILE.MAX_SIZE = 20 ``` ```nginx # nginx.conf client_max_body_size 20M; ```