直接用default_file_storage配置s3/oss会上传失败,因其默认boto3后端不支持分片上传,大文件(>5mb)触发entitytoolarge错误;需改用前端直传或自定义multipart_upload逻辑。

为什么直接用 DEFAULT_FILE_STORAGE 配置 S3/OSS 会上传失败?
因为 Django 4.2 默认的 boto3 后端(storages.backends.s3boto3.S3Boto3Storage)不自动处理分片上传,而海量文件(如 >5MB)在 OSS 或部分 S3 兼容服务(如腾讯云 COS、七牛 Kodo)上必须走 create_multipart_upload 流程,否则会返回 EntityTooLarge 或 InvalidRequest 错误。
实操建议:
- 不要依赖
default_storage.save()直传大文件 —— 它底层调用的是单次put_object,不适用于百 MB 级以上文件 - 改用前端直传(Pre-Signed URL 或 Policy 表单上传),后端只负责生成凭证和校验回调
- 若必须后端中转,需自定义 storage 类,重写
_save()方法并显式调用multipart_upload接口
如何让 Django 4.2 生成兼容 OSS 的 Pre-Signed URL?
OSS 不完全兼容 AWS S3 的签名逻辑:它要求 X-OSS-Date 头、不支持 x-amz- 前缀头、且 region 必须是 oss-{region} 格式(如 oss-cn-hangzhou),不是 cn-hangzhou。
实操建议:
- 用
boto3.client('s3', endpoint_url='https://oss-cn-hangzhou.aliyuncs.com', region_name='oss-cn-hangzhou')初始化客户端 - 生成 URL 时显式指定
Params={'ContentType': 'binary/octet-stream'},避免 OSS 拒绝无 Content-Type 的 PUT - 对阿里云 OSS,必须加
SignatureVersion='s3v4',且config=Config(signature_version='s3v4') - 别用
generate_presigned_post返回的url字段直接拼接 —— OSS 要求 POST 请求发到bucket.endpoint,不是endpoint/bucket
上传回调(Callback)校验失败的三个关键点
Django 接收 OSS/S3 的上传完成回调时,常因签名或 body 解析失败返回 400,导致文件“上传成功但业务未触发”。根本原因是回调 body 是 raw form-data,Django 默认解析不了。
实操建议:
- 视图函数开头加
request.body手动解析,别依赖request.POST - 校验
Authorizationheader 时,OSS 的签名字符串需按POST\n\n{content_type}\n{date}\n{canonicalized_oss_headers}{canonicalized_resource}拼接,注意换行符是\n不是\r\n - 回调中的
callbackVar是 JSON 字符串,但被 URL 编码过,需先urllib.parse.unquote()再json.loads()
为什么 django-storages 的 S3Boto3Storage 在海量并发上传下会卡住?
默认配置下,boto3 使用全局线程安全连接池,但每个 S3Boto3Storage 实例都新建 Session 和 Client,高并发时会耗尽 socket 连接或触发 rate limit。
实操建议:
- 在
settings.py中复用 session:DEFAULT_FILE_STORAGE = 'myapp.storages.OSSStorage',并在自定义类里用session = boto3.Session(...)单例初始化 - 显式配置连接参数:
config=Config(max_pool_connections=50, retries={'max_attempts': 3}) - 禁用
use_ssl=True时,OSS 要求 endpoint 带https://,否则签名失败;但某些内网环境需关 SSL,此时 endpoint 必须用http://且verify=False
最易被忽略的是:OSS 回调服务器 IP 是固定段(如阿里云是 100.64.0.0/10),没加白名单会导致回调超时;S3 无此限制,但 CloudFront 分发时可能隐藏真实源 IP,得靠 X-Forwarded-For 做二次校验。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











