1. В теге изображения указываем другие размеры:
<img src='/path/to/image.jpg' width: 100px height:150px>Очевидно, что в этом случае грузится слишком много не нужного трафика. Не у всех же скоростной интернет, да и глупо это как-то.
2. Заполнитель сайта делает ресайз самостоятельно и загружает обе картинки.
Крайне муторный путь. Но не удивлюсь, если такое где-нибудь практикуется.
3. В административной части при загрузке картинки делается ресайз и обработанные изображения помещаются в нужные папки — xs, s, l, xl и в нужном месте вставляется требуемая картинка
Не всегда размеры, бывают подходящими под задачу — иногда приходится возвращаться к п1. (но уже с меньшими потерями)
4. Прописывать путь к картинке вместе с указанием размера:
path/to/100x150/image.jpg. Натравить nginx на такие пути, если картинки нет — то ресайзить и сохранять. (скриптом)Недостаток такого подхода может быть чрезмерная нагрузка на сервер, в случае если злоумышленник решит создать скрипт в котором будет циклически подставлять различные пути, заставляя сервер ресазить. При желании можно положить сервак, либо забить все свободное место
5. Написать view helper такой как
getImage(100,150,'image.jpg') который проверит наличие изображения и в случае его отсутствия, отресайзит оригинальное и выдаст нужный путь.Недостатком такого подхода является излишний код, и в случае удаления оригинального изображения, отрезайзенные изображения останутся храниться на жестком диске
6. Это способ, который, как мне кажется, решает проблемы, которые я нашел.
/tmp/ /origin/ /crop/100x150/ /crop/200x300/
origin — папка для хранения оригинальных загруженных изображений,
crop — это папка в которой хранятся папки для ресайзнутых изображений.
tmp — папка для временных данных
Демон заглядывает в папку origin, сравнивает дату ее последнего изменения с запомненной датой, и если дата изменения более поздняя чем ту, которую он помнит — то демон запускает режим синхронизации.
Для этого он проходится по папке crop — вытаскивает от туда папки, названия которых совпадают с 000х000 и сравнивает файлы из этой папки с файлами из оригинальной (только названия). Если в кропнутой есть лишние — удаляет их, если недостает — запускает imagic и конвертирует. Размер берет из имени папки.
В
tmp/temp_file сохраняет последнюю отметку времени изменения. Приведу пример реализации этого демона. Написал я его на python. Но сразу предупреждаю, что в python я новичок, поэтому возможны кривые решения. На мой взгляд лучший способ изучить язык — написать на нем хороший проект. Этот демон — часть проекта. Если вы, увидите недоработки — прошу об этом сообщить. Для демонизации используется пакет python-daemon, а на сервере должен быть установлен imagic.
# -*- coding: utf-8 -*- import logging import time import os import re from daemon import runner from sets import Set _PATH = os.path.abspath(os.path.dirname(__file__)) LOG_PATH = os.path.abspath(os.path.join(_PATH, '../', 'files', 'logs')) PID_PATH = os.path.abspath(os.path.join(_PATH, '../', 'files', 'tmp')) class Azazel(): ''' демон азазель, синхронизирует и ресайзит изображения. проверяет изменение в папке origin (новые и удаленные изображения) и раскидывает кропнутые изображения по нужным папкам нужные папки - это папки формата 000x000 находящиеся в папке crop и папки в из дополнительного списка add_sync_folders настройка origin_folder - оригинальная папка sync_folder куда нужно синхронизировать add_sync_folders - дополнительные папки pidfile_path - путь к pid файлу file_save_last_sync_data файл в котором хранится отметка о последней синхронизации демоном LOG_PATH - путь к лог файлу PID_PATH - путь к pid файлу ''' #папка куда нужно синхронизировать sync_folder = os.path.abspath( os.path.join(_PATH, '../', 'files', 'media', 'crop')) # файл в котором храним время # последней синхронизации file_save_last_sync_data = PID_PATH + "/last_update" # папка откуда берем изображения, # которые нужно синхронизировать origin_folder = os.path.abspath( os.path.join(_PATH, '../', 'files', 'media', 'origin')) #дополнительные папки, куда нужно #синхронизировать изображения add_sync_folders = [ os.path.abspath(os.path.join(_PATH, '../', 'files', 'tmp', '100x100')), ] pidfile_path = PID_PATH + '/azazel.pid' stdin_path = '/dev/null' stdout_path = '/dev/tty' stderr_path = '/dev/tty' pidfile_timeout = 5 def __init__(self): self.last_time_update = self.get_last_sync_date() def get_last_update_origin_folder(self): ''' считываем время последнего изменения оригинальной папки ''' last_update = str(os.path.getmtime(self.origin_folder)) return last_update def set_last_update(self, time): ''' записываем в файл время последней синхронизации ''' # сохраним отметку синхронизации self.last_time_update = time # запишем в файл f = open(self.file_save_last_sync_data, 'w') f.write(str(time)) f.close() def get_last_sync_date(self): ''' берем время последней синхронизации из файла ''' f = open(self.file_save_last_sync_data, 'r') last_sync = f.read() f.close() return last_sync def get_folders_to_sync(self): ''' получаем список папок, куда нужно синхронизавть. список берем из dir (где храняться кропнутые изображения) добавляем дополнительные папки (мало-ли, если придется синхронизировать куда-нибудь кроме этого) из списка берем только те которые удовлетвояют виду 000x000 ''' folders = map( lambda folder_name: self.sync_folder + '/' + folder_name, os.listdir(self.sync_folder)) + self.add_sync_folders logger.info("folders list:" + str(folders)) return filter( lambda folder: re.match('^\d{,4}x\d{,4}$', folder.split('/')[-1]), folders) def folder_sync(self, folder): ''' синхронизатор папки. Для папки, мы проходимся и смотрим какие файлы появились в оригинальной и их нужно синхронизировать, какие нужно удалить - если они есть в синхронизируемой, но отсутсвуют в оригинальной ''' logger.info("обработка для папки :" + folder) # файлы в папке folder_image_list = os.listdir(folder) # файлы которые есть в origin_image_list но нет в folder_image_list new_files = Set(self.origin_image_list) - Set(folder_image_list) logger.info("новые файлы :" + str(new_files)) # добавляем for file_to_sync in new_files: #получаем размеры нужного изображения из имени папки size = folder.split('/')[-1] command = 'convert "%s" -resize %s "%s"' % ( self.origin_folder + '/' + file_to_sync, size, folder + '/' + file_to_sync, ) logger.info(command) result = os.system(command) logger.info(result) # файлы которые есть в folder_image_list но нет в origin_image_list delte_files = Set(folder_image_list) - Set(self.origin_image_list) logger.info("устаревшие файлы:" + str(delte_files)) # удаляем for file_to_sync in delte_files: logger.info('delete ' + folder + '/' + file_to_sync) os.remove(folder + '/' + file_to_sync) def sunc_folders(self): ''' проходимся по всем папкам и для кадой запускаем синхронизатор ''' #запоминаем список файлов в оригинальной директории self.origin_image_list = os.listdir(self.origin_folder) #проходим по каждой папке и запускаем синхронизатор for folder in self.get_folders_to_sync(): self.folder_sync(folder) def run(self): logger.info("start daemon azazel") self.sunc_folders() while True: last_update = self.get_last_update_origin_folder() if self.last_time_update < last_update: logger.info("обнаружено изменение") self.sunc_folders() self.set_last_update(last_update) time.sleep(10) daemon = Azazel() logger = logging.getLogger("DaemonLog") logger.setLevel(logging.DEBUG) formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") handler = logging.FileHandler(LOG_PATH + "/azazel.log") handler.setFormatter(formatter) logger.addHandler(handler) daemon_runner = runner.DaemonRunner(daemon) daemon_runner.daemon_context.files_preserve = [handler.stream] daemon_runner.do_action()
— Демон работает прекрасно, проблем не наблюдал, кроме идеологических — возник вопрос, ведь если я не так часто гружу фотографии, зачем нужен демон, и я его переписал. Я учел комментарии и обсуждения и написал как «задачу на celery» которая запускается, при загрузке фотографии. Как-бы общая демонизация осталась, но в ином плане.

