Tengo un modelo de Django que guarda el nombre del archivo como "uuid4().pdf". Donde uuid4 genera un uuid aleatorio para cada instancia creada. Este nombre de archivo también se almacena en el servidor Amazon S3 con el mismo nombre.
Estoy tratando de agregar una disposición personalizada para el nombre de archivo que subo a Amazon s3, esto se debe a que quiero ver un nombre personalizado cada vez que descargo el archivo, no el uuid. Al mismo tiempo, quiero que los archivos se almacenen en s3 con el nombre de archivo uuid.
Entonces, estoy usando django-storages con python 2.7. He intentado agregar content_disposition en configuraciones como esta:
AWS_CONTENT_DISPOSITION = 'core.utils.s3.get_file_name'donde get_file_name() devuelve el nombre del archivo.
También he intentado agregar esto a la configuración:
AWS_HEADERS = { 'Content-Disposition': 'attachments; filename="%s"'% get_file_name(), }¡sin suerte!
¿Alguien de ustedes sabe implementar esto?
La versión actual de S3Boto3Storage de django-storages admite la variable de configuración global AWS_S3_OBJECT_PARAMETERS , que también permite modificar ContentDisposition . Pero el problema es que se aplica tal cual a todos los objetos que se suben al s3 y, además, afecta a todos los modelos que trabajan con el almacenamiento, lo que puede llegar a no ser el resultado esperado.
El siguiente truco funcionó para mí.
from storages.backends.s3boto3 import S3Boto3Storage class DownloadableS3Boto3Storage(S3Boto3Storage): def _save_content(self, obj, content, parameters): """ The method is called by the storage for every file being uploaded to S3. Below we take care of setting proper ContentDisposition header for the file. """ filename = obj.key.split('/')[-1] parameters.update({'ContentDisposition': f'attachment; filename="{filename}"'}) return super()._save_content(obj, content, parameters)Aquí anulamos el método de guardado nativo del objeto de almacenamiento y nos aseguramos de que se establezca la disposición de contenido adecuada en cada archivo. Por supuesto, debe alimentar este almacenamiento al campo en el que está trabajando:
my_file_filed = models.FileField(upload_to='mypath', storage=DownloadableS3Boto3Storage())En caso de que alguien encuentre esto, como lo hice yo: ninguna de las soluciones mencionadas en SO me funcionó en Django 3.0.
Docstring de S3Boto3Storage sugiere anular S3Boto3Storage.get_object_parameters ; sin embargo, este método solo recibe el name del archivo cargado, que en este punto ha sido modificado por upload_to y puede diferir del original.
Lo que funcionó es lo siguiente:
class S3Boto3CustomStorage(S3Boto3Storage): """Override some upload parameters, such as ContentDisposition header.""" def _get_write_parameters(self, name, content): """Set ContentDisposition header using original file name. While docstring recomments overriding `get_object_parameters` for this purpose, `get_object_parameters` only gets a `name` which is not the original file name, but the result of `upload_to`. """ params = super()._get_write_parameters(name, content) original_name = getattr(content, 'name', None) if original_name and name != original_name: content_disposition = f'attachment; filename="{original_name}"' params['ContentDisposition'] = content_disposition return paramsy luego usar este almacenamiento en el campo de archivo, por ejemplo:
file_field = models.FileField( upload_to=some_func, storage=S3Boto3CustomStorage(), ) Independientemente de la solución que se le ocurra, no cambie file_field.storage.object_parameters directamente (por ejemplo, en save() del modelo como se sugirió en una pregunta similar), porque esto cambiará el encabezado ContentDisposition para cargas de archivos posteriores de cualquier campo que use el mismo almacenamiento. Que no es lo que probablemente quieras.
Supongo que está utilizando S3BotoStorage de django-storages , así que mientras carga el archivo en S3, anule el método save() del modelo y establezca el encabezado allí.
Estoy dando un ejemplo a continuación:
class ModelName(models.Model): sthree = S3BotoStorage() def file_name(self,filename): ext = filename.split('.')[-1] name = "%s/%s.%s" % ("downloads", uuid.uuid4(), ext) return name upload_file = models.FileField(upload_to=file_name,storage = sthree) def save(self): self.upload_file.storage.headers = {'Content-Disposition': 'attachments; filename="%s"' %self.upload_file.name} super(ModelName, self).save()