mod: django-filer настройка (11) поддержка heif/heic

This commit is contained in:
2026-06-08 20:06:55 +03:00
parent 7fb5b0e44f
commit 6d6bb873e9
4 changed files with 88 additions and 18 deletions

View File

@@ -5,6 +5,7 @@ from io import BytesIO
from django.apps import AppConfig from django.apps import AppConfig
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from PIL import Image as PILImage from PIL import Image as PILImage
import pillow_heif
from lpon_site.settings import ( from lpon_site.settings import (
THUMBNAIL_WEBP_QUALITY, THUMBNAIL_WEBP_QUALITY,
@@ -15,6 +16,9 @@ from lpon_site.settings import (
FILE_VALIDATORS, FILE_VALIDATORS,
) )
# Регистрируем плагин HEIF в Pillow
pillow_heif.register_heif_opener()
# Получаем логгер для текущего модуля # Получаем логгер для текущего модуля
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -36,10 +40,6 @@ class FrontendConfig(AppConfig):
# ============================================================================== # ==============================================================================
# Кастомная конфигурация для django-filer # Кастомная конфигурация для django-filer
# - Патчинг MultiStorageFieldFile.save() для автоматической конвертации в WebP # - Патчинг MultiStorageFieldFile.save() для автоматической конвертации в WebP
#
# Все параметры конфигурации (MIME_TYPE_WHITELIST, FILER_WHITELIST_FOR_PATH_ACCESS и т.д.)
# определены в settings.py и импортируются оттуда.
# Удаление префикса filer_public_thumbnails/ достигается через THUMBNAIL_OPTIONS в settings.py
# ============================================================================== # ==============================================================================
class CustomFilerConfig(AppConfig): class CustomFilerConfig(AppConfig):
name = 'filer' name = 'filer'
@@ -47,7 +47,6 @@ class CustomFilerConfig(AppConfig):
# ======================================================================== # ========================================================================
# Атрибуты конфигурации Django-Filer (импортированы из settings.py) # Атрибуты конфигурации Django-Filer (импортированы из settings.py)
# Единое место правды - settings.py
# ======================================================================== # ========================================================================
FILER_ENABLE_PERMISSIONS = FILER_ENABLE_PERMISSIONS FILER_ENABLE_PERMISSIONS = FILER_ENABLE_PERMISSIONS
FILER_UPLOADER_MAX_FILE_SIZE = FILER_UPLOADER_MAX_FILE_SIZE FILER_UPLOADER_MAX_FILE_SIZE = FILER_UPLOADER_MAX_FILE_SIZE
@@ -60,10 +59,10 @@ class CustomFilerConfig(AppConfig):
def _convert_to_webp_if_needed(name: str, content): def _convert_to_webp_if_needed(name: str, content):
""" """
Преобразует загруженное изображение в WebP формат. Преобразует загруженное изображение в WebP формат.
Поддерживает JPEG, PNG, BMP и TIFF. Поддерживает JPEG, PNG, BMP, TIFF и HEIC/HEIF.
""" """
_, original_ext = os.path.splitext(name) _, original_ext = os.path.splitext(name)
if original_ext.lower() in [".jpg", ".jpeg", ".png", ".bmp", ".tiff"]: if original_ext.lower() in [".jpg", ".jpeg", ".png", ".bmp", ".tiff", ".heic", ".heif"]:
try: try:
content.seek(0) content.seek(0)
img = PILImage.open(BytesIO(content.read())) img = PILImage.open(BytesIO(content.read()))
@@ -111,4 +110,3 @@ class CustomFilerConfig(AppConfig):
MultiStorageFieldFile.save = patched_save MultiStorageFieldFile.save = patched_save
logger.info("MultiStorageFieldFile.save() patched successfully.") logger.info("MultiStorageFieldFile.save() patched successfully.")

View File

@@ -211,15 +211,20 @@ FILER_MAX_IMAGE_PIXELS = 4096 * 4096
FILER_ENABLE_PERMISSIONS = DEBUG FILER_ENABLE_PERMISSIONS = DEBUG
FILER_WHITELIST_FOR_PATH_ACCESS = ( FILER_WHITELIST_FOR_PATH_ACCESS = (
'.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', '.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', ".heic", ".heif",
'.doc', '.docx', '.pdf', '.txt', '.xls', '.xlsx', '.csv', '.doc', '.docx', '.pdf', '.txt', '.xls', '.xlsx', '.csv',
) )
MIME_TYPE_WHITELIST = ( MIME_TYPE_WHITELIST = (
'image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/webp', 'image/jpeg', # .jpg / .jpeg
'application/pdf', 'application/msword', 'image/png',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'image/gif',
'text/plain', 'application/vnd.ms-excel', 'image/svg+xml',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'image/webp',
'image/heic', 'image/heif', # форматы Apple HEIC/HEIF (без анимации
'application/pdf',
'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', # .doc / .docx
'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', # .xls / .xlsx
'text/plain',
'text/csv', 'text/csv',
) )
FILE_VALIDATORS = {} FILE_VALIDATORS = {}

74
poetry.lock generated
View File

@@ -243,13 +243,13 @@ heif = ["pillow-heif"]
[[package]] [[package]]
name = "django-polymorphic" name = "django-polymorphic"
version = "4.11.4" version = "4.11.5"
description = "Seamless polymorphic inheritance for Django models." description = "Seamless polymorphic inheritance for Django models."
optional = false optional = false
python-versions = "<4.0,>=3.10" python-versions = "<4.0,>=3.10"
files = [ files = [
{file = "django_polymorphic-4.11.4-py3-none-any.whl", hash = "sha256:6093f38ca3c3d25e882d30e2b833f47f2155bd39b7c8c176a7e2e1fc78e6d29f"}, {file = "django_polymorphic-4.11.5-py3-none-any.whl", hash = "sha256:8c65b5c109c057c51e059197b01765dd5e096b3fd393ee3038bffc294681b3be"},
{file = "django_polymorphic-4.11.4.tar.gz", hash = "sha256:2b9bd7c769d34f704465b453cb2ad74fa68b464d6de6091ed65bdfa30650c5f7"}, {file = "django_polymorphic-4.11.5.tar.gz", hash = "sha256:9a6fe66fab12cfe06aac81942d14670c6b0204aebd30ce9fb58fb53213c613eb"},
] ]
[package.dependencies] [package.dependencies]
@@ -533,6 +533,72 @@ test-arrow = ["arro3-compute", "arro3-core", "nanoarrow", "pyarrow"]
tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma (>=5)", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"] tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma (>=5)", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"]
xmp = ["defusedxml"] xmp = ["defusedxml"]
[[package]]
name = "pillow-heif"
version = "1.3.0"
description = "Python interface for libheif library"
optional = false
python-versions = ">=3.10"
files = [
{file = "pillow_heif-1.3.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:0addc7d25133a2abd0149d1f1b8063808268c9ed75dc228c2196c90d56639a25"},
{file = "pillow_heif-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e557b7082e785ff4505b2be258409fc3983162a3802f5438aa3177201d0e5a41"},
{file = "pillow_heif-1.3.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e031f0036200da349cdac1328a3617a9d13b7f9145355c0a36306ce3b9f1d622"},
{file = "pillow_heif-1.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d00218ce66aa74cddb5cb64e59a8867ed6722cc430b240d578ef3bc1307998a3"},
{file = "pillow_heif-1.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4c4eef69c7caec0f41af13308bba5883bde751119f27a51c9a299b83852f3430"},
{file = "pillow_heif-1.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:625b67f7000b3aae645e4aea4170a6f0bb9015577c69cf249c360b10e071e8a7"},
{file = "pillow_heif-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:09a924fbb505674546973518b8906f499a56bb3332752a144bc272becd59c141"},
{file = "pillow_heif-1.3.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:68bea3b9396fdd6c711e66fc645df92bbe53d48892909192ce9a30e4c619878a"},
{file = "pillow_heif-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a59db0091556d11ab26c2b34532b7992965520027ba0a64084771bcc9a31156"},
{file = "pillow_heif-1.3.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b69cc05b4f22ac57f1fd8f5f7ae96ae7c752036659ea975ec5f5565efadd87f"},
{file = "pillow_heif-1.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5a9f26d9ece400f55ac006e2fed079392e44a550023c99122e281fcd72b7c06"},
{file = "pillow_heif-1.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a25a58368222c388a96b111f4621423eac6fa07a0bbcb2ac5eaf624153cde04e"},
{file = "pillow_heif-1.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6d3cdc335df30652ae9b07438e9898ce5cb5dbc47012fa14f93be1df9d446dc5"},
{file = "pillow_heif-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:b5a1458bc11ca83cd72ec4a93f739ad3ae4315ae66766022ec16d12993a863a4"},
{file = "pillow_heif-1.3.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:079abbcaeb42ef0849a33f35c1a96ccd431feb56b242a0d4f8435a1c8ca02c7d"},
{file = "pillow_heif-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76c33f80ec111492642b98309db98516a7fba9677dcda9ec5fe9111b7e38d720"},
{file = "pillow_heif-1.3.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33b838d06e2fd730f806af5a76bfc4cd3de9d146d88d37572e40f7a4c4ff8221"},
{file = "pillow_heif-1.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f92b387af891cf5d98f52e79eeaf51ee7955a54fe2deeec12bfb7519e41464b5"},
{file = "pillow_heif-1.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f9f73246836f93f99343cbc3052b61d212d27e59ddf40262d494a1e3e54af31a"},
{file = "pillow_heif-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84c3816742c2e49176e651895e73c555b9c3b0f3561d60230242f3be0c9d272b"},
{file = "pillow_heif-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:5f0db0bf49162fb1d73d13340a9576b3a2805bde026a9a40038bcc1a0878d710"},
{file = "pillow_heif-1.3.0-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:641c50a064aa9ad6626a6b2b914b65855202f937d573d53838e344feb2e8c6d1"},
{file = "pillow_heif-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9390dd7987887aa09779fbd88bbab715c732c9ad3a71d6707284035e3ca93379"},
{file = "pillow_heif-1.3.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e8444ccb330015e1db930207d269886e4b6c666121cd9e5fdad88735950b09f"},
{file = "pillow_heif-1.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d30054ccc97ecbe5ee3fa486a505ccc33bfbb27f005ad624ddb4c17b80ddd57"},
{file = "pillow_heif-1.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dc1b9c9efdf8345d703118449ff69696d0827bdf28e3b52f82015f5714f7c23e"},
{file = "pillow_heif-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee26b2155721e7f5f7b10fa93ca2ad3be59547c5c5e5d9d50e6ea17531b81d60"},
{file = "pillow_heif-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:17ecbbadfe10ea12a65c1c12354dc1ed8ae1e5d1b7092ea753641b029f7d6f9e"},
{file = "pillow_heif-1.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:8267a73d3b2d07a47a96428bd8cd4c406e1637a94f29d4c16ce08b31b8e50a07"},
{file = "pillow_heif-1.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:36bbea7679467caa3a154db11c04f1ca2fa8591e886f06f40f7831c14b58d771"},
{file = "pillow_heif-1.3.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea3a4b2de4b6c63407af72afdac901616807c6e6a030fe77851d227bca3727a"},
{file = "pillow_heif-1.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05149bd26b08dae5af7a389af6db13cef4f12c7871db73d84e40a1f3c83b0142"},
{file = "pillow_heif-1.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f8b7a50058fc3152f42b68aa2b30601249f61aa5c6c27876af076785c7051fd9"},
{file = "pillow_heif-1.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:edb3ef437e8841475db14721f0529e600bb55c41b549ad1794a0831e28f33bac"},
{file = "pillow_heif-1.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:bdd6695d5be0d98ae0e9a5f88fe26f1a6eca0a5b6d43d0a92a97f89fea5842f7"},
{file = "pillow_heif-1.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:65c5d05cb7f5e1eadbe9c605ae3a4dd3ef953adb33e7d809d5fb56f8a6753588"},
{file = "pillow_heif-1.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:dc177fbdf598770cad4afa99c082a30b9d090e60c39656904338717803ae59b2"},
{file = "pillow_heif-1.3.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71f88d180547bb5112b56310c8c5e338d8358320a402c80afabc6b2f39eadddb"},
{file = "pillow_heif-1.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9acee893186bdde6140d30a7dc6d7c928e4ad3007989764f6e54a7a517faa332"},
{file = "pillow_heif-1.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7cf893689132bec18f0c55a505da9ebf3a8feb33dd354fe2ac050f20f4f862e0"},
{file = "pillow_heif-1.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:54404c9b6f0323114527579f54cc966b47206f99d943e47d73e1091ab0b9d2ba"},
{file = "pillow_heif-1.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:18c7c35a9d98ed9eaaf2db601ee43425ebccc698801df9c008aa04e00756a22e"},
{file = "pillow_heif-1.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2723c4b85c5ad0420cb0b3e512ac0aa015e3c8b13013b4738816833aa431f919"},
{file = "pillow_heif-1.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6bdb197b43a629e2118fd33a9ebcf39abdabe5540b80d8862c53a7611edb42ab"},
{file = "pillow_heif-1.3.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d433048bd23436afa2a33e08fc622712ec97fea1c230d5b5a0fafd5512628c8"},
{file = "pillow_heif-1.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:142b307de087eba33d3174aa29a9669b65a7d006192ef8c98579920be3b56f64"},
{file = "pillow_heif-1.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:633f5d7fbf60a489ba301fbba06e75539a5cd22ff0b036162db2167803416470"},
{file = "pillow_heif-1.3.0.tar.gz", hash = "sha256:af8d2bda85e395677d5bb50d7bda3b5655c946cc95b913b5e7222fabacbb467f"},
]
[package.dependencies]
pillow = ">=11.1.0"
[package.extras]
dev = ["coverage", "defusedxml", "numpy", "opencv-python (==4.13.0.92)", "packaging", "pre-commit", "pylint", "pympler", "pytest", "setuptools"]
docs = ["sphinx (>=4.4)", "sphinx-issues (>=3.0.1)", "sphinx-rtd-theme (>=1.0)"]
tests = ["defusedxml", "numpy", "packaging", "pympler", "pytest"]
tests-min = ["defusedxml", "packaging", "pytest"]
[[package]] [[package]]
name = "python-dotenv" name = "python-dotenv"
version = "1.2.2" version = "1.2.2"
@@ -654,4 +720,4 @@ files = [
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.12" python-versions = "^3.12"
content-hash = "e4e72f02a8c19fc9d396f6d0dd03ea91afbf63da3efc11a77f6e524f5acd9fcc" content-hash = "8a4ac709434f2361f18fff48639d87ea7a0d44b9b6b3de3d99884a3b8814485c"

View File

@@ -14,6 +14,7 @@ django = "^6.0"
python-dotenv = "^1.0.0" python-dotenv = "^1.0.0"
django-environ = "^0.13.0" django-environ = "^0.13.0"
django-filer = "^3.4.4" django-filer = "^3.4.4"
pillow-heif = "^1.3.0"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
django-extensions = "^3.2" django-extensions = "^3.2"