下面给出最精简可跑的「文件」子应用示例,包含:
- 模型:只存文件名 + MinIO 路径
- 3 个接口:上传 / 删除 / 查看(下载)
- 统一返回:{"code":200,"data":{...},"msg":""}
- 使用 django-storages(S3Boto3Storage)方案,与前面 MinIO 配置无缝衔接
- 目录结构(只需关注 file 子应用)
shell
your_project/
├─ file/
│ ├─ migrations/
│ ├─ __init__.py
│ ├─ admin.py
│ ├─ apps.py
│ ├─ models.py
│ ├─ serializers.py
│ ├─ urls.py
│ ├─ views.py
│ └─ utils.py # 可选:生成带签名的下载链接
└─ ...- 安装依赖(已装可跳过)
shell
pip install djangorestframework django-storages boto3- 全局配置(settings.py)
前面 MinIO 配置已写好,再加两项
python
INSTALLED_APPS += ['rest_framework', 'file']
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.MultiPartParser',
]
}- models.py
python
from django.db import models
class File(models.Model):
"""文件元数据"""
name = models.CharField('原始文件名', max_length=255)
path = models.CharField('MinIO 路径', max_length=500, unique=True)
size = models.PositiveIntegerField('字节数')
uploaded_at = models.DateTimeField('上传时间', auto_now_add=True)
def __str__(self):
return self.name- serializers.py
python
from rest_framework import serializers
from .models import File
class FileSerializer(serializers.ModelSerializer):
class Meta:
model = File
fields = '__all__'- views.py
python
import uuid
import os
from django.conf import settings
from django.core.files.storage import default_storage
from rest_framework import status
from rest_framework.decorators import api_view, parser_classes
from rest_framework.parsers import MultiPartParser
from rest_framework.response import Response
from .models import File
from .serializers import FileSerializer
# ---------- 上传 ----------
@api_view(['POST'])
@parser_classes([MultiPartParser])
def upload(request):
file_obj = request.FILES.get('file')
if not file_obj:
return Response({'code': 400, 'msg': '缺少 file 字段'}, status=status.HTTP_400_BAD_REQUEST)
# 生成唯一路径:media/file/2025/10/21/<uuid>.ext
ext = os.path.splitext(file_obj.name)[1]
path = f"media/file/{uuid.uuid4().hex}{ext}"
# 写入 MinIO
saved_path = default_storage.save(path, file_obj)
# 落库存
instance = File.objects.create(
name=file_obj.name,
path=saved_path,
size=file_obj.size
)
return Response({'code': 200, 'data': FileSerializer(instance).data, 'msg': '上传成功'})
# ---------- 删除 ----------
@api_view(['DELETE'])
def delete(request, pk):
try:
f = File.objects.get(pk=pk)
except File.DoesNotExist:
return Response({'code': 404, 'msg': '记录不存在'}, status=status.HTTP_404_NOT_FOUND)
# 先删 MinIO
if default_storage.exists(f.path):
default_storage.delete(f.path)
# 再删库
f.delete()
return Response({'code': 200, 'msg': '删除成功'})
# ---------- 查看 / 下载 ----------
@api_view(['GET'])
def detail(request, pk):
"""返回文件元数据 + 临时下载链接(1 小时有效)"""
try:
f = File.objects.get(pk=pk)
except File.DoesNotExist:
return Response({'code': 404, 'msg': '记录不存在'}, status=status.HTTP_404_NOT_FOUND)
# 生成带签名的 GET URL
url = default_storage.url(f.path) # 如果 settings.AWS_QUERYSTRING_AUTH=True 则自动带签名
data = FileSerializer(f).data
data['url'] = url
return Response({'code': 200, 'data': data, 'msg': ''})- urls.py(子应用级)
python
from django.urls import path
from . import views
urlpatterns = [
path('upload/', views.upload, name='file-upload'),
path('<int:pk>/', views.detail, name='file-detail'),
path('<int:pk>/delete/', views.delete, name='file-delete'),
]- 主路由(project/urls.py)
python
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/file/', include('modules.file.urls')),
]- 迁移 & 运行
shell
python manage.py makemigrations file
python manage.py migrate
python manage.py runserver- 快速测试(curl)
上传
shell
curl -F "file=@/requirements.txt" http://localhost:8000/api/file/upload/查看
shell
curl http://localhost:8000/api/file/1/删除
shell
curl -X DELETE http://localhost:8000/api/file/1/delete/- 可继续完善的点
- 分页列表接口:加
ListAPIView即可 - 权限:接
rest_framework.permissions.IsAuthenticated - 前端直传:MinIO 支持 POST Policy 签名,让浏览器直传 OSS,不走服务器
- 异步删除:若文件大,可用 celery 延迟删除
至此,file 子应用的「上传 / 删除 / 查看」接口已全部就绪,与 MinIO 打通,可直接使用。祝开发顺利!