Django初窥门径-自定义附件存储模型

前言

Django自带了一个名为FileField的字段,用于处理文件上传。然而,有时我们需要更多的控制权,例如定义文件的存储路径、文件名以及文件类型。在本篇文章中,我们将探讨如何自定义Django附件存储模型。

创建attachment应用

bash 复制代码
python manage.py startapp attachment

然后,在项目的settings.py文件中,将应用注册到INSTALLED_APPS列表中,如下所示:

py 复制代码
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'drf_yasg2',
    'django_filters',
    'account.apps.AccountConfig',
    'oauth',
    'attachment'
]

创建模型

定义Attachment

py 复制代码
from django.db import models

# Create your models here.
from rest_framework.reverse import reverse

from CodeVoyager.mixins import BaseModelMixin
import uuid


class BlobField(models.Field):
    description = 'Blob'

    def db_type(self, connection):
        return 'mediumblob'


class Attachment(BaseModelMixin):
    file_id = models.UUIDField(auto_created=True, default=uuid.uuid4, editable=False)
    file_name = models.CharField('文件名', max_length=200, unique=True)
    mime_type = models.CharField('MIME类型', max_length=100)
    file_size = models.PositiveIntegerField('文件长度')
    blob = BlobField('文件内容')

    class Meta:
        verbose_name = '附件'
        verbose_name_plural = verbose_name

    def get_url(self, request):
        return reverse('attachment:download', request=request, kwargs={'attachment_id': self.file_id})
字段名称 类型 用途
file_id UUIDField 存储文件的唯一标识符
file_name CharField 存储文件的名称,即原始文件名
mime_type CharField 存储文件的MIME类型
file_size PositiveIntegerField 存储文件的大小(以字节为单位)
blob 自定义 BlobField 存储文件的二进制内容,即文件的实际数据

将更改应用到数据库

bash 复制代码
python manage.py makemigrations
python manage.py migrate

自定义Django存储

定义存储类

py 复制代码
#!/usr/bin/python
# -*- coding: utf-8 -*-

from django.core.files.base import ContentFile, File
from django.core.files.storage import Storage
from django.utils.deconstruct import deconstructible


@deconstructible
class AttachmentStorage(Storage):
    """附件存储"""

    def __init__(self, model=None):
        from .models import Attachment
        self.model = Attachment

    def _open(self, file_id, mode='rb'):
        instance = self.model.objects.get(file_id=file_id)
        file = ContentFile(instance.blob)
        file.filename = instance.file_name
        file.mimetype = instance.mime_type
        return file

    def _save(self, name, content: File):
        blob = content.read()
        mime_type = getattr(content, 'content_type', 'text/plain')
        self.model.objects.create(
            file_name=name,
            blob=blob,
            file_size=content.size,
            mime_type=mime_type
        )
        return name

    def exists(self, name):
        return self.model.objects.filter(file_name=name).exists()


attachment_storage = AttachmentStorage()
方法名称 参数 返回值 用途
_open file_id, mode='rb' ContentFile 打开文件以供读取,根据给定的file_idAttachment模型中获取文件记录并返回ContentFile对象。
_save name, content: File 文件名 保存文件,将文件名和文件内容作为参数,创建Attachment模型记录并将文件信息保存到数据库。
exists name 布尔值 (TrueFalse) 检查文件是否存在,根据给定的文件名查询Attachment模型,返回True如果文件存在,否则返回False

这些方法共同组成了AttachmentStorage类,用于处理附件文件的存储和访问。_open 方法用于读取文件,_save 方法用于保存文件,而exists 方法用于检查文件是否存在。请注意,初始化方法__init__接受一个model参数。

定义视图

上传视图

py 复制代码
class AttachmentUploadView(APIView):
    permission_classes = (permissions.IsAdminUser,)

    def post(self, request, version):
        try:
            file = request.FILES['file']
        except MultiValueDictKeyError:
            raise ValidationError('参数错误')
        name = attachment_storage.save(file.name, file)
        attachment = get_object_or_404(Attachment, file_name=name)
        return JsonResponse({'download_url': attachment.get_url(request)}, status=status.HTTP_201_CREATED)
  • 作用:处理附件的上传操作。
  • 功能 :当接收到POST请求时,该视图尝试从请求中获取名为 'file' 的文件,然后使用自定义的存储后端 attachment_storage 来保存文件。接着,它在数据库中查找与文件名匹配的附件记录,并返回包含下载链接的 JSON 响应。这个视图的主要目的是允许用户上传附件,并提供上传后的附件的下载链接。

下载视图

py 复制代码
class AttachmentDownloadView(APIView):
    permission_classes = (permissions.IsAuthenticated,)

    def get(self, request, version, attachment_id=None):
        attachment = attachment_storage.open(attachment_id)
        response = HttpResponse(attachment, content_type=attachment.mimetype)
        response['Content-Disposition'] = 'attachment;filename={name}'.format(name=attachment.filename).encode('utf-8')
        return response
  • 作用:处理附件的下载操作。
  • 功能:当接收到GET请求时,该视图使用传递的 attachment_id 参数来打开相应的附件。然后,它创建一个包含附件内容的 HTTP 响应对象,设置响应的内容类型为附件的 MIME 类型,并设置响应头 Content-Disposition,指定附件的文件名。最后,它返回包含附件内容的 HTTP 响应。这个视图的主要目的是允许用户通过提供附件的唯一标识符来下载附件。

Content-Disposition 是一个HTTP响应头,它用于指示浏览器如何处理接收到的文件。具体来说,Content-Disposition 头的值告诉浏览器应该如何处理响应的内容,通常用于文件下载操作。

注册视图

py 复制代码
#!/usr/bin/python
# -*- coding: utf-8 -*-
from django.urls import re_path, path

from .views import AttachmentUploadView, AttachmentDownloadView

app_name = 'attachment'

urlpatterns = [
    re_path(r'upload', AttachmentUploadView.as_view(), name='upload'),
    path(r'download/<uuid:attachment_id>', AttachmentDownloadView.as_view(), name='download'),
]

使用swagger测试接口

上传

下载

结语

在开发Web应用程序时,文件上传和下载是常见的功能之一,但同时也需要特别关注安全性。通过合理的安全性配置,你可以保护应用程序和用户的数据免受潜在的威胁。在实际的项目中,我们可以增加一些重要的安全性措施,包括文件类型验证、文件大小限制、CSRF保护、存储路径安全性和其他关键措施,以确保文件上传和下载功能的安全性。

相关推荐
老邓计算机毕设19 分钟前
SSM智慧社区家政服务系统80q7o(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm 框架
松涛和鸣1 小时前
72、IMX6ULL驱动实战:设备树(DTS/DTB)+ GPIO子系统+Platform总线
linux·服务器·arm开发·数据库·单片机
likangbinlxa2 小时前
【Oracle11g SQL详解】UPDATE 和 DELETE 操作的正确使用
数据库·sql
r i c k2 小时前
数据库系统学习笔记
数据库·笔记·学习
野犬寒鸦2 小时前
从零起步学习JVM || 第一章:类加载器与双亲委派机制模型详解
java·jvm·数据库·后端·学习
IvorySQL3 小时前
PostgreSQL 分区表的 ALTER TABLE 语句执行机制解析
数据库·postgresql·开源
·云扬·3 小时前
MySQL 8.0 Redo Log 归档与禁用实战指南
android·数据库·mysql
IT邦德3 小时前
Oracle 26ai DataGuard 搭建(RAC到单机)
数据库·oracle
惊讶的猫4 小时前
redis分片集群
数据库·redis·缓存·分片集群·海量数据存储·高并发写
不爱缺氧i4 小时前
完全卸载MariaDB
数据库·mariadb