Добавить динамическое расположение содержимого для имен файлов (amazon S3) в python

У меня есть модель Django, которая сохраняет имя файла как «uuid4().pdf». Где uuid4 генерирует случайный uuid для каждого созданного экземпляра. Это имя файла также хранится на сервере amazon s3 с тем же именем.

Я пытаюсь добавить пользовательское расположение для имени файла, которое я загружаю на amazon s3, потому что я хочу видеть пользовательское имя всякий раз, когда я загружаю файл, а не uuid. В то же время я хочу, чтобы файлы хранились на s3 с именем файла uuid.

Итак, я использую django-storages с python. 2.7. Я попытался добавить content_disposition в такие настройки:

AWS_CONTENT_DISPOSITION = 'core.utils.s3.get_file_name'

где get_file_name() возвращает имя файла.

Я также попытался добавить это в настройки:

AWS_HEADERS = {
'Content-Disposition': 'attachments; filename="%s"'% get_file_name(),

 }

не повезло!

Кто-нибудь из вас знает, как это реализовать.


person Yaman Ahlawat    schedule 04.04.2017    source источник


Ответы (3)


Текущая версия S3Boto3Storage от django-storages поддерживает глобальную переменную настроек AWS_S3_OBJECT_PARAMETERS, которая также позволяет изменять ContentDisposition. Но проблема в том, что он применяется как есть ко всем объектам, которые загружаются в s3 и, более того, затрагивает все модели, работающие с хранилищем, что может обернуться не ожидаемым результатом.

У меня сработал следующий хак.

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)

Здесь мы переопределяем собственный метод сохранения объекта хранилища и обеспечиваем правильное расположение содержимого для каждого файла. Конечно, вам нужно скормить это хранилище тому полю, над которым вы работаете:

my_file_filed = models.FileField(upload_to='mypath', storage=DownloadableS3Boto3Storage())
person abcdn    schedule 28.05.2019

Я предполагаю, что вы используете S3BotoStorage из django-storages, поэтому при загрузке файла на S3 переопределите метод save() модели и установите там заголовок.

Я привожу пример ниже:

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()
person Sibasish    schedule 07.02.2018
comment
Это можно настроить для условной установки заголовков, только если вы используете S3BotoStorage. При этом вы можете легко создавать тесты, не заботясь о S3. Вот почему это требует моего голоса. - person rafaelpivato; 29.04.2020

На случай, если кто-то обнаружит это, как и я: ни одно из решений, упомянутых в SO, не сработало для меня в Django 3.0.

Строка документации S3Boto3Storage предлагает переопределить S3Boto3Storage.get_object_parameters, однако этот метод получает только name загруженного файла, который на данный момент изменен на upload_to и может отличаться от оригинала.

Что сработало, так это следующее:

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 params

а затем использовать это хранилище в поле файла, например:


    file_field = models.FileField(
        upload_to=some_func,
        storage=S3Boto3CustomStorage(),
    )

Какое бы решение вы ни предложили, не изменяйте file_field.storage.object_parameters напрямую (например, в save() модели, как было предложено в аналогичном вопросе), потому что это изменит заголовок ContentDisposition для последующих загрузок файла любого поля, которое использует такое же хранилище. Это не то, что вы, вероятно, хотите.

person Anna Sirota    schedule 08.12.2020