签入版本

This commit is contained in:
2026-09-12 23:12:15 +08:00
commit 91998056ca
1841 changed files with 341325 additions and 0 deletions
@@ -0,0 +1,223 @@
# 文件上传完整流程
<!-- Copyright (c) 2021-2026 小蚂蚁云团队 版权所有 -->
本章从配置到使用,完整说明文件上传功能的实现方式,包括后端接口、前端组件、安全机制和最佳实践。
## 上传架构
```
前端 Vue3 后端 Django 文件系统
┌──────────┐ ┌──────────────┐ ┌──────────┐
│ Upload │ multipart │ 安全校验 │ 写入 │ uploads/ │
│ 组件 │ ─────────> │ ─ 扩展名白名单│ ───────> │ temp/ │
│ │ │ ─ MIME 校验 │ │ 日期/ │
│ │ │ ─ 大小校验 │ │ xxx.jpg│
└──────────┘ └──────────────┘ └──────────┘
│
▼
┌──────────────┐
│ 返回 fileUrl │
│ 前端存入字段 │
└──────────────┘
```
## 环境变量配置
在 `config/env.py` 中配置:
| 变量 | 默认值 | 说明 |
|:----|:----|:----|
| `DJANGO_UPLOAD_DIR` | `{项目根目录}/public/uploads` | 上传文件存储目录 |
| `DJANGO_FILE_URL` | `http://file.django.elevue` | 文件访问域名 |
| `DJANGO_TEMP_PATH` | `{DJANGO_UPLOAD_DIR}/temp` | 临时文件目录 |
## 上传接口
系统提供统一的文件上传入口,支持图片、文档、压缩包等各类文件:
### 上传文件
```
POST /upload/uploadFile
Content-Type: multipart/form-data
Authorization: Bearer <token>
file: <文件>
```
支持格式:
```
图片:.jpeg, .jpg, .png, .gif, .bmp
文档:.pdf, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .txt
```
成功响应:
```json
{
"code": 0,
"msg": "上传成功",
"data": {
"originalName": "photo.jpg",
"fileExtension": "jpg",
"fileType": "image/jpeg",
"fileSize": 306995,
"fileName": "2025030614302512345.jpg",
"filePath": "/temp/20250306/2025030614302512345.jpg",
"fileUrl": "http://file.django.elevue/temp/20250306/2025030614302512345.jpg"
},
"ok": true
}
```
:::tip 富文本编辑器集成
富文本编辑器(如 WangEditor 等)的图片上传同样使用此接口。编辑器配置上传地址为 `/upload/uploadFile`,上传成功后从 `response.data.fileUrl` 获取图片 URL 即可。
:::
## 安全机制
### 扩展名白名单
只允许指定的扩展名格式上传,拒绝危险格式(如 `.exe`、`.bat`、`.sh`、`.js` 等)。
### MIME 类型校验
通过 `mimetypes` 模块推断文件的 MIME 类型,确保文件内容与扩展名一致。
### 临时目录隔离
上传的文件首先存储在 `temp` 临时目录,业务数据提交时才迁移到正式目录。未提交的临时文件可定期清理。
## 文件存储策略
### 目录结构
```
public/uploads/
├── temp/ # 临时文件目录
│ ├── 20260905/
│ │ ├── 2026090514302512345.jpg
│ │ └── 2026090515011278901.pdf
│ └── 20260906/
├── example/ # 正式文件目录(按业务分类)
│ ├── 20260905/
│ │ └── 2026090514302512345.jpg
│ └── 20260906/
└── article/
└── 20260906/
```
- 按日期分目录存储(`{日期}/`),避免单目录文件过多
- 文件名使用 `年月日时分秒 + 5位随机数` 重命名,避免中文文件名和冲突
- 保留原始扩展名
### 文件访问 URL
上传成功后返回的 `fileUrl` 是拼接了 `DJANGO_FILE_URL` 前缀的完整 URL:
```
http://file.django.elevue/temp/20250306/2025030614302512345.jpg
```
Service 层的 `get_file_url()` 会自动为文件字段补此前缀。
## 前端集成
### 文件上传组件
```vue
<template>
<el-upload
:action="uploadUrl"
:headers="headers"
:on-success="handleSuccess"
:before-upload="beforeUpload"
>
<el-button type="primary">上传文件</el-button>
</el-upload>
</template>
<script setup>
import { ref } from 'vue';
import { useUserStore } from '@/store/modules/user';
const uploadUrl = '/api/upload/uploadFile';
const userStore = useUserStore();
const headers = {
Authorization: `Bearer ${userStore.token}`,
};
function beforeUpload(file) {
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
ElMessage.error('文件大小不能超过 10MB');
}
return isLt10M;
}
function handleSuccess(response) {
if (response.code === 0) {
// 保存 fileUrl 到业务字段
form.value.cover = response.data.fileUrl;
}
}
</script>
```
## 业务模块中的文件字段
在 Service 中使用 `save_file()` 处理文件迁移:
```python
# application/example/services.py
from utils.common import save_file, get_file_url
def add_example(request):
data, error = parse_request_body(request)
# ...
avatar = cleaned_data.get('avatar')
if avatar:
avatar = save_file(avatar, "example") # 迁移到正式目录
Example.objects.create(avatar=avatar, ...)
```
### 自动处理逻辑
1. **新增/更新时**:检测文件字段值是否为临时路径,如果是则迁移到正式目录
2. **列表/详情时**:通过 `get_file_url()` 自动为文件字段补全 `DJANGO_FILE_URL` 前缀
## 富文本中的图片
使用 `save_content()` 处理富文本中的图片:
```python
# utils/common.py
def save_content(content, title, directory):
"""提取富文本中的图片并迁移,替换 URL"""
image_urls = re.findall('img src="(.*?)"', content, re.S)
for url in image_urls:
image = save_file(url, directory)
if image:
content = content.replace(url, "[IMG_URL]" + image)
return content
```
## 常见问题
### 上传报演示环境无权限
**原因**:演示模式下写操作被拦截。
**解决**:将 `.env` 中的 `DJANGO_DEMO` 设为 `False`。
### 图片上传成功但不显示
**原因**:`DJANGO_FILE_URL` 配置错误或前端未拼接域名。
**解决**:检查 `config/env.py` 中的 `DJANGO_FILE_URL` 配置,确保前端正确拼接。
## 总结
文件上传功能通过扩展名白名单 + MIME 类型校验 + 临时目录隔离等安全机制保障安全性。文件按日期分目录存储,使用时间戳 + 随机数重命名。统一的 `uploadFile` 接口支持图片和各类文件上传。Service 层通过 `save_file()` 和 `save_content()` 处理文件迁移和 URL 拼接。