基于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
相关产品推荐
相关产品推荐

