Django Storages S3 是一项面向资深 Django 开发者的技能,用于通过 django-storages 和 boto3 在 AWS S3 上实现生产级文件存储,涵盖公开与私有媒体文件、静态文件、预签名 URL 以及 CloudFront。
适用场景
- 从 AWS S3 而非本地文件系统提供静态文件和/或媒体文件
- 配置 Django 4.2+ 的 STORAGES 字典或旧版 DEFAULT_FILE_STORAGE
- 分离公开(CDN 提供)与私有(预签名)文件后端
- 生成预签名下载 URL 或浏览器直传 S3 的 URL
- 为 S3 前置 CloudFront 并编写最小权限 IAM 策略
- 在不修改代码的情况下将本地 FileField/ImageField 存储迁移到 S3
- 在不实际访问 S3 的情况下测试存储代码
核心工作流
- 安装与注册 —— 执行 pip install django-storages[s3] boto3;将 "storages" 加入 INSTALLED_APPS
- 配置凭证 —— 从环境变量加载或依赖附加的 IAM 角色;切勿硬编码
- 连接 STORAGES 字典 —— 为默认(媒体)与 staticfiles 后端设置各自的 location 前缀
- 添加命名后端 —— 需要时在 STORAGES 中额外配置公开与私有桶/ACL 条目
- 验证与测试 —— 运行 collectstatic,确认上传落入 S3,并在测试中用 InMemoryStorage 或 moto 模拟 S3
参考指南
按上下文加载详细指引:
- 设置与 STORAGES:references/configuration.md(核心设置、4.2+ 与旧版对比、CloudFront)
- 自定义后端:references/custom-backends.md(公开与私有桶、按字段存储)
- 预签名 URL:references/presigned-urls.md(下载链接、浏览器直传)
- 测试与 IAM:references/testing-storages.md(模拟 S3、IAM 策略、常见陷阱)
最小可用示例
以下片段展示了核心的必须遵守约束:从环境变量加载凭证、STORAGES 字典、媒体与静态分离的 location,以及媒体后端上的 default_acl=None。
# settings.py
import os
AWS_STORAGE_BUCKET_NAME = os.environ["AWS_STORAGE_BUCKET_NAME"]
AWS_S3_REGION_NAME = os.environ.get("AWS_S3_REGION_NAME", "us-east-1")
AWS_S3_CUSTOM_DOMAIN = f"{AWS_STORAGE_BUCKET_NAME}.s3.{AWS_S3_REGION_NAME}.amazonaws.com"
# On EC2/ECS/Lambda, omit keys entirely — boto3 uses the attached IAM role.
STORAGES = {
"default": { # media uploads
"BACKEND": "storages.backends.s3boto3.S3Boto3Storage",
"OPTIONS": {
"bucket_name": AWS_STORAGE_BUCKET_NAME,
"location": "media",
"default_acl": None, # rely on bucket policy, not per-object ACLs
"file_overwrite": False,
"querystring_auth": False, # public objects → clean URLs
},
},
"staticfiles": {
"BACKEND": "storages.backends.s3boto3.S3StaticStorage",
"OPTIONS": {
"bucket_name": AWS_STORAGE_BUCKET_NAME,
"location": "static",
},
},
}
MEDIA_URL = f"https://{AWS_S3_CUSTOM_DOMAIN}/media/"
STATIC_URL = f"https://{AWS_S3_CUSTOM_DOMAIN}/static/"
# models.py — uploads go straight to S3 on save()
from django.db import models
class Document(models.Model):
file = models.FileField(upload_to="docs/") # uses STORAGES["default"]
审查现有配置
在审查已使用 S3 的项目(非全新项目)时,按以下清单逐项检查,每项都是将约束改写为“查找 X,确认 Y”:
- 凭证 —— grep -rn "AWS_SECRET_ACCESS_KEY|aws_secret" settings/ → 确认值来自 os.environ/django-environ 或 IAM 角色,而非提交到仓库的字面量。
- ACL —— grep -rn "default_acl|AWS_DEFAULT_ACL" . → 在 2023 年 4 月后创建的桶上,所有值必须为 None。任何 "public-read"/"private" 都会引发 AccessControlListNotSupported;公开访问应放在桶策略中。
- 存储后端 —— 确认 Django 4.2+ 使用 STORAGES 字典,而非 DEFAULT_FILE_STORAGE/STATICFILES_STORAGE(在 Django 5.1 中已移除,因此在 5.1/5.2/6.0 上会被静默忽略);确认静态类为 S3StaticStorage,而非虚构名称。
- Locations —— 确认默认(媒体)与 staticfiles 具有不同的 location 前缀或不同的桶,避免 collectstatic 与上传冲突。
- 区域 —— 确认 region_name(或全局 AWS_S3_REGION_NAME)与桶的真实区域一致,并确认对于非 us-east-1 的桶,AWS_S3_CUSTOM_DOMAIN 包含区域段。
- 预签名 —— 对于私有后端,确认 querystring_auth=True 且 custom_domain=None;确认预签名的 .url() 结果不会在 AWS_QUERYSTRING_EXPIRE 之后仍被缓存。
- 覆盖清理 —— 在 file_overwrite=False 的情况下,确认被替换的文件被显式删除(否则被取代的对象会泄漏)。
- IAM —— 确认策略仅授予桶 ARN 上的 Get/Put/Delete/ListBucket,而非更广泛的 S3 访问权限。
约束
必须做:
- 从环境变量或附加的 IAM 角色加载 AWS 凭证
- 设置 default_acl=None,使桶策略(而非对象 ACL)控制访问
- 为静态文件与媒体文件提供不同的 location 前缀或不同的桶
- 在 Django 4.2+ 上使用 STORAGES 字典(相同配置适用于 5.2 LTS 与 6.0);DEFAULT_FILE_STORAGE/STATICFILES_STORAGE 在 5.1 中已移除,仅保留给 < 4.2
- 在任何签发预签名 URL 的后端上设置 custom_domain=None
- 在测试中模拟 S3(InMemoryStorage 或 moto),而非访问真实桶
不可做:
- 在 settings.py 中硬编码 AWS_SECRET_ACCESS_KEY 或将其提交
- 将 querystring_auth=True 与 custom_domain 混用(会破坏预签名)
- 将静态文件与媒体文件混用同一前缀
- 授予 IAM 用户超出桶 ARN 上 Get/Put/Delete/ListBucket 的权限
- 在 2023 年 4 月后创建的桶上依赖逐对象 ACL(ACL 默认已禁用)
知识参考
django-storages、S3Boto3Storage、S3StaticStorage、boto3、STORAGES dict、预签名 URL、generate_presigned_post、CloudFront、IAM 策略、InMemoryStorage、moto
相关技能
- django-expert —— 核心 Django 模型、DRF 与 ORM,产生本技能持久化到 S3 的文件
- fullstack-guardian —— 围绕已存储文件的安全端到端上传流程与访问控制
- devops-engineer —— 为本技能目标配置 S3 桶、IAM 角色与 CloudFront 分发