Бывают случаи, когда нужно на сайте вывести уменьшенную копию изображения. Возможные решения:
1. В теге изображения указываем другие размеры:
Очевидно, что в этом случае грузится слишком много не нужного трафика. Не у всех же скоростной интернет, да и глупо это как-то.
2. Заполнитель сайта делает ресайз самостоятельно и загружает обе картинки.
Крайне муторный путь. Но не удивлюсь, если такое где-нибудь практикуется.
3. В административной части при загрузке картинки делается ресайз и обработанные изображения помещаются в нужные папки — xs, s, l, xl и в нужном месте вставляется требуемая картинка
Не всегда размеры, бывают подходящими под задачу — иногда приходится возвращаться к п1. (но уже с меньшими потерями)
4. Прописывать путь к картинке вместе с указанием размера:
Недостаток такого подхода может быть чрезмерная нагрузка на сервер, в случае если злоумышленник решит создать скрипт в котором будет циклически подставлять различные пути, заставляя сервер ресазить. При желании можно положить сервак, либо забить все свободное место
5. Написать view helper такой как
Недостатком такого подхода является излишний код, и в случае удаления оригинального изображения, отрезайзенные изображения останутся храниться на жестком диске
6. Это способ, который, как мне кажется, решает проблемы, которые я нашел.
origin — папка для хранения оригинальных загруженных изображений,
crop — это папка в которой хранятся папки для ресайзнутых изображений.
tmp — папка для временных данных
Демон заглядывает в папку origin, сравнивает дату ее последнего изменения с запомненной датой, и если дата изменения более поздняя чем ту, которую он помнит — то демон запускает режим синхронизации.
Для этого он проходится по папке crop — вытаскивает от туда папки, названия которых совпадают с 000х000 и сравнивает файлы из этой папки с файлами из оригинальной (только названия). Если в кропнутой есть лишние — удаляет их, если недостает — запускает imagic и конвертирует. Размер берет из имени папки.
В
Приведу пример реализации этого демона. Написал я его на python. Но сразу предупреждаю, что в python я новичок, поэтому возможны кривые решения. На мой взгляд лучший способ изучить язык — написать на нем хороший проект. Этот демон — часть проекта. Если вы, увидите недоработки — прошу об этом сообщить. Для демонизации используется пакет python-daemon, а на сервере должен быть установлен imagic.
— Демон работает прекрасно, проблем не наблюдал, кроме идеологических — возник вопрос, ведь если я не так часто гружу фотографии, зачем нужен демон, и я его переписал. Я учел комментарии и обсуждения и написал как «задачу на celery» которая запускается, при загрузке фотографии. Как-бы общая демонизация осталась, но в ином плане.
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» которая запускается, при загрузке фотографии. Как-бы общая демонизация осталась, но в ином плане.