Skip to content

下面给出最精简可跑的「文件」子应用示例,包含:

  • 模型:只存文件名 + MinIO 路径
  • 3 个接口:上传 / 删除 / 查看(下载)
  • 统一返回:{"code":200,"data":{...},"msg":""}
  • 使用 django-storages(S3Boto3Storage)方案,与前面 MinIO 配置无缝衔接

  1. 目录结构(只需关注 file 子应用)

shell
your_project/  
├─ file/  
  ├─ migrations/  
  ├─ __init__.py  
  ├─ admin.py  
  ├─ apps.py  
  ├─ models.py  
  ├─ serializers.py  
  ├─ urls.py  
  ├─ views.py  
  └─ utils.py   # 可选:生成带签名的下载链接  
└─ ...

  1. 安装依赖(已装可跳过)

shell
pip install djangorestframework django-storages boto3

  1. 全局配置(settings.py)

前面 MinIO 配置已写好,再加两项

python
INSTALLED_APPS += ['rest_framework', 'file']
REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.MultiPartParser',
    ]
}

  1. 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

  1. serializers.py

python
from rest_framework import serializers
from .models import File

class FileSerializer(serializers.ModelSerializer):
    class Meta:
        model = File
        fields = '__all__'

  1. 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': ''})

  1. 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'),
]

  1. 主路由(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')),
]

  1. 迁移 & 运行

shell
python manage.py makemigrations file
python manage.py migrate
python manage.py runserver

  1. 快速测试(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/

  1. 可继续完善的点

  • 分页列表接口:加 ListAPIView 即可
  • 权限:接 rest_framework.permissions.IsAuthenticated
  • 前端直传:MinIO 支持 POST Policy 签名,让浏览器直传 OSS,不走服务器
  • 异步删除:若文件大,可用 celery 延迟删除

至此,file 子应用的「上传 / 删除 / 查看」接口已全部就绪,与 MinIO 打通,可直接使用。祝开发顺利!