Files
octopus/myproject/products/utils/image_service.py

140 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Сервис для получения URL изображений разных размеров.
Используется в шаблонах и представлениях для удобного доступа к разным версиям.
"""
import os
from django.conf import settings
from django.core.files.storage import default_storage
class ImageService:
"""
Сервис для работы с изображениями разных размеров.
Динамически строит URL на основе пути к оригинальному файлу.
МУЛЬТИТЕНАНТНОСТЬ:
На диске файлы хранятся как: tenants/{tenant_id}/products/{entity_id}/{photo_id}/{size}.ext
В БД сохраняется путь БЕЗ tenant_id: products/{entity_id}/{photo_id}/{size}.ext
TenantAwareFileSystemStorage автоматически добавляет/удаляет tenant_id при работе с файлами.
ImageService работает с путями из БД и генерирует URL, при необходимости системе видны файлы только своего тенанта.
"""
# Константы для маппинга форматов и расширений файлов
FORMAT_EXTENSIONS = {
'JPEG': 'jpg',
'WEBP': 'webp',
'PNG': 'png',
}
@staticmethod
def _get_config():
"""Получить конфигурацию из settings"""
return getattr(settings, 'IMAGE_PROCESSING_CONFIG', {})
@staticmethod
def _get_format_config(size_key):
"""Получить конфигурацию формата для заданного типа изображения"""
config = ImageService._get_config()
formats = config.get('formats', {})
return formats.get(size_key, {'format': 'JPEG', 'quality': 90})
@staticmethod
def _get_file_extension(image_format):
"""Получить расширение файла для заданного формата"""
return ImageService.FORMAT_EXTENSIONS.get(image_format, 'jpg')
@staticmethod
def _normalize_size_name(size_key):
"""Преобразовать 'thumbnail' в 'thumb' для имени файла"""
return 'thumb' if size_key == 'thumbnail' else size_key
@staticmethod
def get_url(original_image_path, size='medium'):
"""
Получает URL изображения нужного размера.
Структура хранения: base_path/entity_id/photo_id/size.ext
Пример: products/123/456/medium.webp
Args:
original_image_path: Путь к оригинальному файлу (из models.image)
Пример: products/123/456/original.jpg
size: Размер ('original', 'large', 'medium', 'thumbnail')
По умолчанию 'medium'
Returns:
str: URL изображения или пустая строка если нет файла
"""
if not original_image_path:
return ''
try:
path_str = str(original_image_path)
parts = path_str.split('/')
if len(parts) < 3:
return default_storage.url(path_str)
# Если изображение во временной папке (еще не обработано)
# Возвращаем оригинал, так как других размеров еще нет
if 'temp' in parts:
return default_storage.url(path_str)
# Извлекаем base_path, entity_id, photo_id из пути
base_path = parts[0] # products, kits, categories
entity_id = parts[1] # ID сущности
photo_id = parts[2] # ID фото
# Определяем расширение из конфигурации
format_config = ImageService._get_format_config(size)
image_format = format_config.get('format', 'JPEG')
extension = ImageService._get_file_extension(image_format)
# Преобразуем thumbnail в thumb
final_size_name = ImageService._normalize_size_name(size)
# Создаем путь и используем storage.url() для корректной работы с tenant-aware storage
file_path = f"{base_path}/{entity_id}/{photo_id}/{final_size_name}.{extension}"
# Используем default_storage.url() для корректной работы с TenantAwareFileSystemStorage
# Это гарантирует что URL будет содержать tenant_id если необходимо
return default_storage.url(file_path)
except Exception:
return ''
@staticmethod
def get_thumbnail_url(original_image_path):
"""Получить URL миниатюры (150x150)"""
return ImageService.get_url(original_image_path, 'thumbnail')
@staticmethod
def get_medium_url(original_image_path):
"""Получить URL среднего размера (400x400)"""
return ImageService.get_url(original_image_path, 'medium')
@staticmethod
def get_large_url(original_image_path):
"""Получить URL большого размера (800x800)"""
return ImageService.get_url(original_image_path, 'large')
@staticmethod
def get_original_url(original_image_path):
"""Получить URL оригинального изображения"""
return ImageService.get_url(original_image_path, 'original')
@staticmethod
def get_all_urls(original_image_path):
"""
Получить все версии изображения.
Returns:
dict: {'original': url, 'thumbnail': url, 'medium': url, 'large': url}
"""
return {
'original': ImageService.get_original_url(original_image_path),
'thumbnail': ImageService.get_thumbnail_url(original_image_path),
'medium': ImageService.get_medium_url(original_image_path),
'large': ImageService.get_large_url(original_image_path),
}