Django+S3存储:如何优雅获取文件元数据中的原始文件名?
解决方案
1. 修复自定义字段报错:正确实现FieldFile与FileField
Django中,模型实例的文件字段返回的是FieldFile对象而非FileField本身,所以需要将元数据获取方法定义在自定义FieldFile类中,再关联到自定义FileField:
from django.db import models from storages.backends.s3boto3 import S3Boto3Storage class S3FieldFile(models.FileField.attr_class): def get_original_name(self): """从S3元数据中提取原始文件名""" # 复用django-storages已配置好的S3客户端,无需手动初始化boto3 s3_client = self.storage.connection.meta.client try: head_obj = s3_client.head_object( Bucket=self.storage.bucket_name, Key=self.name ) # 元数据不存在时返回当前存储文件名作为兜底 return head_obj['Metadata'].get('original_name', self.name) except s3_client.exceptions.ClientError: return self.name class S3FileField(models.FileField): # 指定使用自定义的FieldFile类 attr_class = S3FieldFile
在模型中使用该自定义字段:
class Document(models.Model): file = S3FileField(upload_to=upload_to, storage=CustomS3Storage()) # 其他模型字段...
之后即可直接通过模型实例调用方法获取原始文件名:
doc = Document.objects.get(id=1) original_name = doc.file.get_original_name()
2. 优化元数据设置(避免并发冲突)
你之前在upload_to中修改全局object_parameters的方式存在线程安全隐患,推荐改为在自定义Storage类中为单个文件单独设置元数据:
import os import uuid from storages.backends.s3boto3 import S3Boto3Storage class CustomS3Storage(S3Boto3Storage): def _save(self, name, content): # 从上传文件对象中获取原始文件名 original_name = content.name # 为当前文件单独设置元数据,不影响全局配置 self.object_parameters['Metadata'] = {'original_name': original_name} return super()._save(name, content)
对应的upload_to方法只需专注生成存储路径:
def upload_to(instance, filename): # 生成唯一文件名避免冲突 ext = os.path.splitext(filename)[1] return f"documents/{uuid.uuid4().hex}{ext}"
报错原因说明
之前的AttributeError是因为仅自定义了S3FileField但未指定attr_class,导致访问document.file时返回的仍是默认FieldFile对象,找不到你定义的方法。通过指定attr_class为自定义S3FieldFile,即可让字段实例拥有对应的元数据获取方法。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

