Become a sponsor

本章从配置到使用,完整说明文件上传功能的实现方式,包括后端接口、前端组件、安全机制和最佳实践。
前端 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成功响应:
{
"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
}富文本编辑器集成
富文本编辑器(如 WangEditor 等)的图片上传同样使用此接口。编辑器配置上传地址为 /upload/uploadFile,上传成功后从 response.data.fileUrl 获取图片 URL 即可。
只允许指定的扩展名格式上传,拒绝危险格式(如 .exe、.bat、.sh、.js 等)。
通过 mimetypes 模块推断文件的 MIME 类型,确保文件内容与扩展名一致。
上传的文件首先存储在 temp 临时目录,业务数据提交时才迁移到正式目录。未提交的临时文件可定期清理。
public/uploads/
├── temp/ # 临时文件目录
│ ├── 20260905/
│ │ ├── 2026090514302512345.jpg
│ │ └── 2026090515011278901.pdf
│ └── 20260906/
├── example/ # 正式文件目录(按业务分类)
│ ├── 20260905/
│ │ └── 2026090514302512345.jpg
│ └── 20260906/
└── article/
└── 20260906/{日期}/),避免单目录文件过多年月日时分秒 + 5位随机数 重命名,避免中文文件名和冲突上传成功后返回的 fileUrl 是拼接了 DJANGO_FILE_URL 前缀的完整 URL:
http://file.django.elevue/temp/20250306/2025030614302512345.jpgService 层的 get_file_url() 会自动为文件字段补此前缀。
<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() 处理文件迁移:
# 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, ...)get_file_url() 自动为文件字段补全 DJANGO_FILE_URL 前缀使用 save_content() 处理富文本中的图片:
# 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 拼接。