Django 亮点:处理静态资产和媒体文件(第 4 部分)

已发表: 2022-03-10
快速总结↬前端开发人员和设计人员为 Web 应用程序创建了惊人的静态资产。 今天,我们将重点关注在您刚刚完成的样式修补程序或精美图形被推送到大师之后会发生什么。 我们还将调查处理用户上传的文件,称为媒体文件。 我们将一起开发一种直觉,了解 Django 开发人员可用的策略,以便以安全、高性能和经济高效的方式向全球用户提供这些文件。

Django 网站涉及很多文件。 它不仅仅是配置、模型、视图和模板的源代码,还包括静态资产:CSS 和 JavaScript、图像、图标。 好像这还不够,有时用户会出现并希望将自己的文件上传到您的网站。 这足以让任何开发人员难以置信。 文件无处不在!

这就是我希望我可以说的地方(没有警告):“别担心,Django 支持你!” 但不幸的是,在处理静态资产和媒体文件时,需要处理很多注意事项。

今天,我们将讨论为单服务器和可扩展部署存储和提供文件,同时考虑压缩、缓存和可用性等因素。 我们还将讨论 CDN 和专用文件存储解决方案的成本和收益。

注意这不是关于如何将 Django 站点部署到任何特定平台的教程。 相反,与 Django Highlights 系列中的其他文章(见下文)一样,它旨在作为前端开发人员和设计人员了解创建 Web 应用程序过程的其他部分的指南。 今天,我们将重点关注在您刚刚完成的样式修补程序或精美图形被推送到大师之后会发生什么。 我们将一起开发一种直觉,了解 Django 开发人员可用的策略,以便以安全、高性能和经济高效的方式向全球用户提供这些文件。

该系列的先前部分

  • 第 1 部分:用户模型和身份验证
  • 第 2 部分:模板保存行
  • 第 3 部分:模型、管理和利用关系数据库
跳跃后更多! 继续往下看↓

定义

这些术语中的大多数都非常简单,但值得花点时间为本次讨论建立一个共享的词汇表。

实时 Django 应用程序中的三种文件类型是:

  1. 源代码
    使用 Django 框架创建的 Python 和 HTML 文件。 这些文件是应用程序的核心。 源代码文件通常很小,以千字节为单位。
  2. 静态文件
    这些文件也称为“静态资产”,包括由应用程序开发人员和第三方库编写的 CSS 和 JavaScript,以及 PDF、软件安装程序、图像、音乐、视频和图标。 这些文件仅在客户端使用。 静态文件的范围从几千字节的 CSS 到几千兆字节的视频。
  3. 媒体文件
    用户上传的任何文件,从个人资料图片到个人文档,都称为媒体文件。 需要为用户安全可靠地存储和检索这些文件。 媒体文件可以是任意大小,用户可以将几千字节的明文上传到几千兆字节的视频中。 如果您处于此规模的后期,您可能需要比本文准备提供的更专业的建议。

两种类型的 Django 部署是:

  1. 单服务器
    单服务器 Django 部署正是它听起来的样子:一切都存在于单个服务器上。 这种策略非常简单,与开发环境非常相似,但无法有效处理大量或不一致的流量。 单服务器方法仅适用于学习或演示项目,不适用于需要可靠正常运行时间的实际应用程序。
  2. 可扩展
    部署 Django 项目的方法有很多种,可以扩展以满足用户需求。 这些策略通常涉及启动和关闭大量服务器以及使用负载平衡器和托管数据库等工具。 幸运的是,为了本文的目的,我们可以有效地将比单服务器部署更复杂的所有内容归为此类。

选项 1:默认 Django

小型项目受益于简单的架构。 Django 对静态资源和媒体文件的默认处理就是:简单。 对于每一个,您都有一个根文件夹,用于存储文件并位于服务器上的源代码旁边。 简单的。 这些根文件夹主要通过yourproject/settings.py配置生成和管理。

静态资产

在 Django 中处理静态文件时要了解的最重要的事情是python manage.py collectstatic命令。 此命令遍历 Django 项目中每个应用程序的静态文件夹,并将所有静态资产复制到根文件夹。 运行此命令是部署 Django 项目的重要部分。 考虑以下目录结构:

 - project - project - settings.py - urls.py - ... - app1 - static/ - app1 - style.css - script.js - img.jpg - templates/ - views.py - ... - app2 - static/ - app2 - style.css - image.png - templates/ - views.py - ...

还假设project/settings.py中的以下设置:

 STATIC_URL = "/static/" STATIC_ROOT = "/path/on/server/to/djangoproject/static"

运行python manage.py collectstatic命令将在服务器上创建以下文件夹:

 - /path/on/server/to/djangoproject/static - app1 - style.css - script.js - img.jpg - app2 - style.css - image.png

请注意,在每个静态文件夹中,都有另一个带有应用程序名称的文件夹。 这是为了防止收集静态文件后的命名空间冲突; 正如您在上面的文件结构中看到的那样,这使app1/style.cssapp2/style.css保持不同。 从这里开始,应用程序将在生产期间在STATIC_ROOT的此结构中查找静态文件。 因此,在app1/templates/的模板中引用静态文件如下:

 {% load static %} <link rel="stylesheet" type="text/css" href="{% static "app1/style.css" %}">

Django 会自动找出在开发过程中从何处获取静态文件来建模此行为,您无需在开发过程中运行collectstatic

有关更多详细信息,请参阅 Django 文档。

媒体文件

想象一个具有用户数据库的专业网站。 这些用户中的每一个都会有一个关联的个人资料,其中可能包含头像图像和简历文档等。 这是该信息的简短示例模型:

 from django.db import models from django.contrib.auth.models import User def avatar_path(instance, filename): return "avatar_{}_{}".format(instance.user.id, filename) class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) resume = models.FileField(upload_to="path/string") avatar = models.ImageField(upload_to=avatar_path)

为此,您需要project/settings.py中的以下选项,例如静态资产:

 MEDIA_URL = "/media/" MEDIA_ROOT = "/path/on/server/to/media"

ImageField继承自FileField ,因此它共享相同的参数和功能。 这两个字段都有一个可选的upload_to参数,它接受一个作为路径的字符串并将其附加到MEDIA_ROOT以存储文件,然后可以通过MEDIA_URL顶部的相同路径访问该文件。 upload_to参数也可以采用返回字符串的函数,如avatar_path函数所示。

确保从版本控制中省略媒体文件目录及其内容。 当两个开发人员在不同的机器上测试同一个应用程序时,它的内容可能会发生冲突,并且与静态资产不同,它不是可部署的 Django 应用程序的一部分。

选项 2:带有服务的 Django

我的指导理念是使用工具做他们最擅长的事情。 Django 是一个了不起的框架,它为用户身份验证、服务器端渲染、处理模型和表单、管理功能以及构建 Web 应用程序的许多其他基本方面提供了开箱即用的出色工具。 但是,在我看来,它用于处理静态资产和媒体文件的工具并不适合在可扩展站点上进行制作。 Django 核心开发人员认识到,许多人选择替代方法来处理生产中的这些文件; 当您这样做时,该框架非常擅长摆脱困境。 大多数用于一般用途的 Django 站点都希望合并静态资产并使用这些非 Django 特定的方法处理媒体文件。

CDN 上的静态资产

虽然中小型项目没有一个也可以逃脱,但 CDN(内容交付网络)易于使用并提高了任何规模的应用程序的性能。 CDN 是一个服务器网络,通常在全球范围内分发和提供 Web 内容,主要是静态资产。 流行的 CDN 包括 Cloudflare CDN、Amazon CloudFront 和 Fastly。 要使用 CDN,请上传静态文件,然后在应用程序中按如下方式引用它们:

 <link rel="stylesheet" type="text/css" href="https://cdn.example.com/path/to/your/files/app1/style.css">

这个过程很容易与您的 Django 部署脚本集成。 运行python manage.py collectstatic命令后,将生成的目录复制到您的 CDN(根据您使用的服务而有很大差异的过程),然后从 Django 部署包中删除静态资产。

在开发中,您需要访问与生产中不同的静态资产副本。 这样,您可以在本地进行更改而不会影响生产站点。 您可以使用本地资产或运行 CDN 的第二个实例来交付文件。 使用一些自定义变量(例如CDN_URL )配置yourproject/settings.py ,并在模板中使用该值以确保您在开发和生产中使用正确版本的资产。

最后一点是,许多 CSS 和 JavaScript 库都有大多数网站可以使用的免费 CDN。 如果您正在加载,例如,Bootstrap 4 或 underscore.js,您可以跳过在开发中使用自己的副本的麻烦,以及通过使用这些公共 CDN 在生产中提供自己的副本的费用。

具有专用文件存储的媒体文件

任何生产 Django 站点都不应该将用户文件存储在运行站点的服务器上某个简单的/media/文件夹中。 以下是为什么不这样做的众多原因中的三个:

  1. 如果您需要通过添加多个服务器来扩展站点,则需要某种方式在这些服务器之间复制和同步上传的文件。
  2. 如果服务器崩溃,源代码会在您的版本控制系统中备份,但默认情况下不会备份媒体文件,除非您将服务器配置为这样做,但为此您最好使用专用的文件存储。
  3. 如果发生恶意活动,最好将用户上传的文件保存在与运行应用程序的服务器不同的服务器上,尽管这绝不会消除验证用户上传文件的要求。

集成第三方来存储用户上传的文件非常简单。 您不需要更改代码中的任何内容,除了可能删除或修改模型中FileFieldupload_to值以及配置一些设置。 例如,如果您计划将文件存储在 AWS S3 中,则需要执行以下操作,这与使用 Google Cloud、Azure、Backblaze 或类似竞争服务存储文件的过程非常相似。

首先,您需要安装库boto3django-storages storages 。 然后,您需要在 AWS 上设置存储桶和 IAM 角色,这超出了本文的范围,但您可以在此处查看说明。 配置完所有这些后,您需要在project/settings.py中添加三个变量:

 DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage" AWS_STORAGE_BUCKET_NAME = "BUCKET_NAME" AWS_S3_REGION_NAME = "us-east-2"

此外,您需要设置对 AWS 存储桶的凭证访问。 一些教程将演示如何将 ID 和密钥添加到您的设置文件或作为环境变量,但这些都是不安全的做法。 相反,使用django-storages和 AWS CLI 来配置密钥,如此处所述。 您可能还对django-storages文档感兴趣。

您不希望开发或测试媒体文件与实际用户上传的内容混淆。 避免这种情况非常简单:设置多个存储桶,一个用于开发(或每个开发人员一个),一个用于测试,一个用于生产。 然后,您只需更改每个环境的AWS_STORAGE_BUCKET_NAME设置,一切顺利。

性能和可用性

有许多因素会影响您网站的性能和可靠性。 在考虑无论您采用哪种方法管理它们都很重要的静态和媒体文件时,这里有一些重要的。

成本

向用户提供文件需要花钱有两个原因:存储和带宽。 您必须向托管服务提供商付费才能为您存储文件,但您也必须付费才能提供文件。 带宽比存储贵得多(例如,在撰写本文时,AWS S3 的存储费用为每 GB 2.3 美分,而传输到 Internet 的数据为每 GB 9 美分)。 像 S3 或 CDN 这样的文件存储的经济性与像 Digital Ocean 水滴这样的通用主机的经济性不同。 通过将昂贵的文件转移到专为他们设计的服务来利用专业化和规模经济。 此外,许多文件存储和 CDN 提供免费计划,因此可能小到不使用它们就可以逃脱的站点可以这样做并获得收益,而无需任何额外的基础设施成本。

压缩和转码

由照片和视频等静态资产引起的大多数问题是因为它们是大文件。 自然,开发人员通过尝试使这些文件更小来解决这个问题。 有许多方法可以在两大类中混合使用压缩和转码:无损和有损。 无损压缩保留了资产的原始质量,但文件大小相对较小。 有损压缩,或转码为有损格式,允许更小的文件大小,但会损失一些原始工件的质量。 这方面的一个例子是将视频转码为较低的比特率。 有关详细信息,请查看这篇关于优化视频传输的文章。 通过 Web 提供大型文件时,带宽速度通常要求您提供高度压缩的工件,这需要有损压缩。

除非您是 YouTube,否则压缩和转码不会即时进行。 静态资产应在部署前进行适当格式化,并且您可以对用户上传的文件强制执行基本文件类型和文件大小限制,以确保用户的媒体文件有足够的压缩和适当的格式。

缩小

虽然 JavaScript 和 CSS 文件通常不像图像那么大,但它们通常可以被压缩以压缩成更少的字节。 这个过程称为缩小。 缩小不会改变文件的编码,它们仍然是文本,并且缩小的文件仍然需要是其原始语言的有效代码。 缩小文件保留其原始扩展名。

在缩小文件中删除的主要内容是不必要的空格,从计算机的角度来看,CSS 和 JavaScript 中几乎所有的空格都是不必要的。 缩小方案还会缩短变量名称并删除注释。

默认情况下缩小混淆代码; 作为开发人员,您应该专门使用非缩小文件。 部署过程中的一些自动步骤应在存储和提供文件之前缩小文件。 如果您使用的是第三方 CDN 提供的库,请确保您使用的是该库的缩小版本(如果可用)。 HTML 文件可以缩小,但由于 Django 使用服务器端渲染,因此动态执行此操作的处理成本很可能超过页面大小的小幅减小。

全球可用性

就像向邻居发送一封信比在全国范围内发送一封信所花费的时间更少一样,在附近传输数据所花费的时间也比在世界各地传输数据所花费的时间更少。 CDN 提高页面性能的方法之一是将资产复制到世界各地的服务器上。 然后,当客户端发出请求时,它们会从最近的服务器(通常称为边缘节点)接收静态资产,从而减少加载时间。 将 CDN 与 Django 站点一起使用的优点之一是将静态资产的全球分布与代码的全球分布分离。

客户端缓存

有什么比在用户附近的服务器上拥有静态文件更好的呢? 已将静态文件存储在用户的设备上! 缓存是存储计算或请求结果的过程,以便可以更快地重复访问它们。 就像 CSS 样式表可以在 CDN 中缓存在世界各地一样,它可以在客户端第一次从您的站点加载页面时缓存在客户端的浏览器中。 然后,样式表在后续请求中可在设备本身上使用,因此客户端发出的请求更少,页面加载时间缩短,带宽使用减少。

浏览器执行它们自己的缓存操作,但是如果您的站点享有大量流量,您可以使用 Django 的缓存框架优化您的客户端缓存行为。

综上所述

同样,我的指导理念是使用工具来做他们最擅长的事情。 单服务器项目和只有轻量级静态资产的小型可扩展部署可以使用 Django 内置的静态资产管理,但大多数应用程序应该分离出资产以通过 CDN 提供服务。

如果您的项目旨在用于任何类型的实际使用,请不要使用 Django 的默认方法存储媒体文件,而是使用服务。 有了足够的流量,“足够的流量”在互联网规模上是一个相对较小的数字,架构、开发过程和部署的额外复杂性对于使用分别为静态和媒体文件提供单独的 CDN 和文件存储解决方案。

推荐阅读

  • 第 1 部分:用户模型和身份验证
  • 第 2 部分:模板保存行
  • 第 3 部分:模型、管理和利用关系数据库