You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Django REST Framework与MySQL的PDF上传下载后端实现及存储咨询

Django REST Framework实现PDF上传下载及存储方案建议

一、DRF后端实现PDF上传与下载步骤

1. 模型定义

创建模型存储PDF的元数据和文件引用,按年月归档便于管理:

from django.db import models

class PDFDocument(models.Model):
    title = models.CharField(max_length=255, verbose_name="文档标题")
    file = models.FileField(upload_to='pdfs/%Y/%m/', verbose_name="PDF文件")
    uploaded_at = models.DateTimeField(auto_now_add=True, verbose_name="上传时间")

    def __str__(self):
        return self.title

2. 序列化器编写

序列化器返回文件可访问URL,避免暴露原始文件字段:

from rest_framework import serializers
from .models import PDFDocument

class PDFDocumentSerializer(serializers.ModelSerializer):
    file_url = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = PDFDocument
        fields = ['id', 'title', 'file_url', 'uploaded_at']

    def get_file_url(self, obj):
        request = self.context.get('request')
        if obj.file and request:
            return request.build_absolute_uri(obj.file.url)
        return None

3. 视图实现

用视图集处理上传、列表、下载逻辑,增加文件类型校验:

from rest_framework import viewsets, status
from rest_framework.response import Response
from rest_framework.decorators import action
from django.http import FileResponse
from django.core.exceptions import ObjectDoesNotExist
from .models import PDFDocument
from .serializers import PDFDocumentSerializer

class PDFDocumentViewSet(viewsets.ModelViewSet):
    queryset = PDFDocument.objects.all().order_by('-uploaded_at')
    serializer_class = PDFDocumentSerializer

    def create(self, request, *args, **kwargs):
        uploaded_file = request.FILES.get('file')
        if uploaded_file and not uploaded_file.name.lower().endswith('.pdf'):
            return Response(
                {"error": "仅支持上传PDF格式文件"},
                status=status.HTTP_400_BAD_REQUEST
            )
        return super().create(request, *args, **kwargs)

    @action(detail=True, methods=['get'], url_path='download')
    def download_pdf(self, request, pk=None):
        try:
            document = PDFDocument.objects.get(pk=pk)
        except ObjectDoesNotExist:
            return Response(
                {"error": "指定文档不存在"},
                status=status.HTTP_404_NOT_FOUND
            )
        
        response = FileResponse(
            document.file.open('rb'),
            content_type='application/pdf'
        )
        response['Content-Disposition'] = f'attachment; filename="{document.file.name.split("/")[-1]}"'
        return response

4. 基础配置

  • 本地存储:在settings.py中配置媒体文件路径,根路由添加访问规则:
# settings.py
import os
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

# 根urls.py
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    # 其他路由
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
  • S3存储:安装django-storages和boto3后,配置AWS参数:
# settings.py
INSTALLED_APPS = [
    # 其他应用
    'storages',
]

AWS_ACCESS_KEY_ID = '你的AWS访问密钥ID'
AWS_SECRET_ACCESS_KEY = '你的AWS秘密访问密钥'
AWS_STORAGE_BUCKET_NAME = '你的S3存储桶名称'
AWS_S3_REGION_NAME = '存储桶所在区域(如ap-southeast-1)'
AWS_S3_FILE_OVERWRITE = False
AWS_DEFAULT_ACL = 'private'
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

二、存储方案对比:S3 vs MySQL

1. S3存储PDF是否合适?

完全合适,是生产环境的推荐方案:

  • 专为二进制文件优化:支持无限扩容,不受服务器磁盘限制;
  • 性能与成本优势:可配合CloudFront CDN加速全球下载,按需付费成本更低;
  • 安全与管理便捷:自带版本控制、生命周期归档、细粒度权限控制,无需自行开发;
  • 避免数据库膨胀:仅在数据库存储元数据和S3地址,不影响数据库查询、备份性能。

2. 仅在MySQL中存储PDF是否可行?

技术上可行,但不推荐用于生产环境:

  • 实现方式:用BinaryField存储PDF二进制数据:
class PDFDocument(models.Model):
    title = models.CharField(max_length=255)
    file_data = models.BinaryField(verbose_name="PDF二进制数据")
    uploaded_at = models.DateTimeField(auto_now_add=True)
  • 核心缺点:
    • 数据库性能下降:大体积BLOB会导致表膨胀,查询、备份速度大幅降低;
    • 后端负载升高:每次下载需从数据库读取二进制数据再返回,占用大量连接和带宽;
    • 功能缺失:无法直接HTTP访问,无CDN、版本控制等高级功能,维护成本高。

内容的提问来源于stack exchange,提问作者Chang-Man

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 17:53:21