django-storage 文件上传SuspiciousOperation 和joined path is located outside the base path component error
django-storage file upload SuspiciousOperation and the joined path is located outside of the base path component error
我正在使用最新版本的 django 和 django-storage。我正在尝试保存上传的图像和本地生成的调整大小的图像。当我尝试保存图像时出现以下错误:
回溯:
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))
根据另一个 Whosebug 的回答,我已将此解决方案添加到所有客户存储中:
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'
修复:
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
我正在使用最新版本的 django 和 django-storage。我正在尝试保存上传的图像和本地生成的调整大小的图像。当我尝试保存图像时出现以下错误:
回溯:
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))
根据另一个 Whosebug 的回答,我已将此解决方案添加到所有客户存储中:
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'
修复:
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