Загрузка файла django-хранилища SuspiciousOperation и присоединенный путь находятся за пределами ошибки компонента базового пути - PullRequest
0 голосов
/ 23 июня 2019

Я использую последнюю версию django и django-storage. Я пытаюсь сохранить загруженное изображение и локально сгенерированное изображение с измененным размером. Когда я попытался сохранить изображение, я получил следующие ошибки:

Traceback:

File "/home/..proj/.env/lib/python3.6/site-packages/storages/backends/s3boto3.py" in _normalize_name
  431.             return safe_join(self.location, name)

File "/home/prism/Desktop/code/viper/.env/lib/python3.6/site-packages/storages/utils.py" in safe_join
  75.         raise ValueError('the joined path is located outside of the base path'

During handling of the above exception (the joined path is located outside of the base path component), another exception occurred:

File "/home/..proj/.env/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/home/..proj/.env/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "/home/..proj/.env/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/home/..proj/.env/lib/python3.6/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
  21.                 return view_func(request, *args, **kwargs)

File "/home/..proj/admin/views.py" in product_update
  578.                                 image_model.image_thumbnail_index.save(a.name, File(a))

File "/home/..proj/.env/lib/python3.6/site-packages/django/db/models/fields/files.py" in save
  87.         self.name = self.storage.save(name, content, max_length=self.field.max_length)

File "/home/..proj/.env/lib/python3.6/site-packages/django/core/files/storage.py" in save
  51.         name = self.get_available_name(name, max_length=max_length)

File "/home/..proj/.env/lib/python3.6/site-packages/storages/backends/s3boto3.py" in get_available_name
  633.         return super(S3Boto3Storage, self).get_available_name(name, max_length)

File "/home/..proj/.env/lib/python3.6/site-packages/django/core/files/storage.py" in get_available_name
  75.         while self.exists(name) or (max_length and len(name) > max_length):
File "/home/..proj/.env/lib/python3.6/site-packages/storages/backends/s3boto3.py" in exists
  528.         name = self._normalize_name(self._clean_name(name))

File "/home/..proj/.env/lib/python3.6/site-packages/storages/backends/s3boto3.py" in _normalize_name
  434.                                       name)

Exception Type: SuspiciousOperation at /admin/product/update/7/
Exception Value: Attempted access to '/home/..proj/convert/mqedpqL4tepvF4bT7wySMm/jo_308x412.webp' denied.

Модель изображения:

class ProductImage(models.Model):
    image = models.ImageField(storage=ProductMediaStorage())
    image_thumbnail_index = models.ImageField(storage=ProductMediaStorage())
    image_quickview = models.ImageField(storage=MediaStorage())

Пользовательское хранилище S3:

class ProductMediaStorage(S3Boto3Storage):
    location = settings.AWS_PRODUCT_LOCATION
    default_acl = 'public-read'
    file_overwrite = False

class MediaStorage(S3Boto3Storage):
    location = settings.AWS_MEDIA_LOCATION
    default_acl = 'public-read'
    file_overwrite = False

Настройки:

AWS_ACCESS_KEY_ID = JSON_DATA['aws_access_key']
AWS_SECRET_ACCESS_KEY = JSON_DATA['aws_secret_key']
AWS_STORAGE_BUCKET_NAME = JSON_DATA['aws_bucket']
AWS_DEFAULT_ACL = None
AWS_IS_GZIPPED = True
AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com'
AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',
}

AWS_PRODUCT_LOCATION = 'product'
AWS_MEDIA_LOCATION = 'media'


if not DEBUG:
    STATIC_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{AWS_STATIC_LOCATION}/'
    MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/media/'
    STATICFILES_STORAGE = 'hiren.storage.StaticStorage'
    DEFAULT_FILE_STORAGE = 'hiren.storage.DefaultStorage'
else:
    MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/media/'
    STATIC_URL = '/static/'
    STATIC_ROOT = os.path.join(BASE_DIR, 'collect_static')
    STATICFILES_DIRS = (
        os.path.join(BASE_DIR, "static"),
    )
    DEFAULT_FILE_STORAGE = 'hiren.storage.DefaultStorage'

# uploaded file settings

FILE_UPLOAD_MAX_MEMORY_SIZE = 0

Views.py:

def product_update(request):
   product_file = request.FILES.getlist('image', None)

   if product_file:
      for pro in product_file:
           uuid = shortuuid.uuid()
           directory = BASE_DIR + '/convert/' + uuid
           os.mkdir(directory)
           fs = FileSystemStorage(location=directory)
           filename = fs.save(pro.name, pro)
           image_sizes = ["308x412", "400x400" ]
           files = resize_convert(filename, image_sizes, uuid)
           # print(files)  -->  {"308x412": "/home/.../proj/img location"}
           image_model = ProductImage(image=pro)
           with open(files["308x412"], 'rb') as a, open(files["400x400"], 'rb') as b:
               image_model.image_thumbnail_index.save(a.name, File(a))
               image_model.image_quickview.save(b.name, File(b))

Из другого ответа stackoverflow Я добавил это решение во все хранилища клиентов:

class MediaStorage(S3Boto3Storage):
    location = settings.AWS_MEDIA_LOCATION
    default_acl = 'public-read'
    file_overwrite = False

    def _clean_name(self, name):
        return name

    def _normalize_name(self, name):
        if not name.endswith('/'):
            name += "/"

        name += self.location
        return name

Тогда я получил эту ошибку:

File "/home/..proj/.env/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/home/..proj/.env/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "/home..proj/.env/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/home/..proj/.env/lib/python3.6/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
  21.                 return view_func(request, *args, **kwargs)

File "/home/..proj/admin/views.py" in product_update
  578.                                 image_model.image_thumbnail_index.save(a.name, File(a))

File "/home/..proj/.env/lib/python3.6/site-packages/django/db/models/fields/files.py" in save
  93.             self.instance.save()

File "/home/..proj/.env/lib/python3.6/site-packages/django/db/models/base.py" in save
  741.                        force_update=force_update, update_fields=update_fields)

File "/home/..proj/.env/lib/python3.6/site-packages/django/db/models/base.py" in save_base
  779.                 force_update, using, update_fields,

File "/home/..proj/.env/lib/python3.6/site-packages/django/db/models/base.py" in _save_table
  870.             result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)

File "/home/..proj/.env/lib/python3.6/site-packages/django/db/models/base.py" in _do_insert
  908.                                using=using, raw=raw)

File "/home/..proj/.env/lib/python3.6/site-packages/django/db/models/manager.py" in manager_method
  82.                 return getattr(self.get_queryset(), name)(*args, **kwargs)

File "/home/..proj/.env/lib/python3.6/site-packages/django/db/models/query.py" in _insert
  1186.         return query.get_compiler(using=using).execute_sql(return_id)

File "/home/..proj/.env/lib/python3.6/site-packages/django/db/models/sql/compiler.py" in execute_sql
  1334.             for sql, params in self.as_sql():

File "/home/..proj/.env/lib/python3.6/site-packages/django/db/models/sql/compiler.py" in as_sql
  1278.                 for obj in self.query.objs

File "/home/..proj/.env/lib/python3.6/site-packages/django/db/models/sql/compiler.py" in <listcomp>
  1278.                 for obj in self.query.objs

File "/home/..proj/.env/lib/python3.6/site-packages/django/db/models/sql/compiler.py" in <listcomp>
  1277.                 [self.prepare_value(field, self.pre_save_val(field, obj)) for field in fields]

File "/home/..proj/.env/lib/python3.6/site-packages/django/db/models/sql/compiler.py" in pre_save_val
  1228.         return field.pre_save(obj, add=True)

File "/home/..proj/.env/lib/python3.6/site-packages/django/db/models/fields/files.py" in pre_save
  288.             file.save(file.name, file.file, save=False)

File "/home..proj/.env/lib/python3.6/site-packages/django/db/models/fields/files.py" in save
  87.         self.name = self.storage.save(name, content, max_length=self.field.max_length)

File "/home/..proj/.env/lib/python3.6/site-packages/django/core/files/storage.py" in save
  52.         return self._save(name, content)

File "/home/..proj/.env/lib/python3.6/site-packages/storages/backends/s3boto3.py" in _save
  506.         self._save_content(obj, content, parameters=parameters)

File "/home/..proj/.env/lib/python3.6/site-packages/storages/backends/s3boto3.py" in _save_content
  521.         obj.upload_fileobj(content, ExtraArgs=put_parameters)

File "/home/..proj/.env/lib/python3.6/site-packages/boto3/s3/inject.py" in object_upload_fileobj
  621.         ExtraArgs=ExtraArgs, Callback=Callback, Config=Config)

File "/home/..proj/.env/lib/python3.6/site-packages/boto3/s3/inject.py" in upload_fileobj
  539.         return future.result()

File "/home/..proj/.env/lib/python3.6/site-packages/s3transfer/futures.py" in result
  106.             return self._coordinator.result()

File "/home/..proj/.env/lib/python3.6/site-packages/s3transfer/futures.py" in result
  265.             raise self._exception

File "/home/..proj/.env/lib/python3.6/site-packages/s3transfer/tasks.py" in __call__
  126.                 return self._execute_main(kwargs)

File "/home/..proj/.env/lib/python3.6/site-packages/s3transfer/tasks.py" in _execute_main
  150.         return_value = self._main(**kwargs)

File "/home/..proj/.env/lib/python3.6/site-packages/s3transfer/upload.py" in _main
  692.             client.put_object(Bucket=bucket, Key=key, Body=body, **extra_args)

File "/home/..proj/.env/lib/python3.6/site-packages/s3transfer/utils.py" in __exit__
  525.         self.close()

File "/home/..proj/.env/lib/python3.6/site-packages/s3transfer/utils.py" in close
  508.         self._fileobj.close()

File "/home/..proj/.env/lib/python3.6/site-packages/s3transfer/upload.py" in close
  95.         self._fileobj.close()

File "/home/..proj/.env/lib/python3.6/tempfile.py" in close
  650.         self._closer.close()

File "/home/..proj/.env/lib/python3.6/tempfile.py" in close
  587.                         unlink(self.name)

Exception Type: FileNotFoundError at /admin/product/update/7/
Exception Value: [Errno 2] No such file or directory: '/tmp/tmpfnqmmwq8.upload.jpeg'

1 Ответ

0 голосов
/ 26 июня 2019

Исправлено:

class ProductMediaStorage(S3Boto3Storage):
    location = settings.AWS_PRODUCT_LOCATION
    default_acl = 'public-read'
    file_overwrite = False

    def _save_content(self, obj, content, parameters):
        """
        We create a clone of the content file as when this is passed to boto3 it wrongly closes
        the file upon upload where as the storage backend expects it to still be open
        """
        # Seek our content back to the start
        content.seek(0, os.SEEK_SET)

        # Create a temporary file that will write to disk after a specified size
        content_autoclose = SpooledTemporaryFile()

        # Write our original content into our copy that will be closed by boto3
        content_autoclose.write(content.read())

        # Upload the object which will auto close the content_autoclose instance
        super(ProductMediaStorage, self)._save_content(obj, content_autoclose, parameters)

        # Cleanup if this is fixed upstream our duplicate should always close
        if not content_autoclose.closed:
            content_autoclose.close()

    def _normalize_name(self, name):
        # if not name.endswith('/'):
        #     name += "/"
        #
        # name += self.location
        return name
...