📜 ⬆️ ⬇️

Solving the problem of operational image resizing

There are cases when you need to display a small copy of the image on the site. Possible solutions:

1. In the image tag specify other sizes:
<img src='/path/to/image.jpg' width: 100px height:150px>
Obviously, in this case too much unnecessary traffic is loaded. Not everyone has high-speed Internet, and it’s stupid somehow.

2. The site placeholder resizes itself and loads both images.
Extremely dreary way. But do not be surprised if this is practiced anywhere.

3. In the administrative part, when downloading an image, a resize is done and the processed images are placed in the right folders - xs, s, l, xl and the required image is inserted in the right place
Dimensions are not always appropriate for the task - sometimes you have to go back to n1. (but already with less losses)
')
4. Register the path to the picture along with the size: path/to/100x150/image.jpg path/to/100x150/image.jpg . To set nginx on such paths, if there is no picture, then resize and save. (script)

The disadvantage of this approach can be an excessive load on the server, in case the attacker decides to create a script in which he will cyclically substitute various paths, causing the server to resazit. If you wish, you can put a server, or to fill the entire free space.

5. Write a view helper such as getImage(100,150,'image.jpg') that will check for the presence of the image and, if it is not there, remove the original and produce the desired path.

The disadvantage of this approach is redundant code, and in case of deleting the original image, the recovered images will remain stored on the hard disk.

6. This is the way that I think solves the problems I found.

 /tmp/ /origin/ /crop/100x150/ /crop/200x300/ 


origin - the folder for storing the original downloaded images,
crop is the folder in which folders for resized images are stored.
tmp - temporary data folder

The demon looks at the origin folder, compares the date of its last change with the memorized date, and if the change date is later than the one he remembers, then the daemon starts the synchronization mode.

To do this, it goes through the crop folder - pulls out folders from there, the names of which coincide with 000x000 and compares the files from this folder with the files from the original (only the names). If there are extras in the crooked - removes them; if not, it launches imagic and converts. Size takes from the folder name.
The tmp/temp_file saves the last timestamp of the change.

I will give an example of the implementation of this demon. I wrote it in python. But I warn you right away that I am a newbie in python, so solution curves are possible. In my opinion the best way to learn a language is to write a good project on it. This demon is part of the project. If you see gaps, please report it. For demonization, the python-daemon package is used, and imagic should be installed on the server.

 # -*- 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() 


- The demon works perfectly, I did not observe any problems, except for ideological ones - a question arose, because if I don’t load photos so often, why do I need a demon and I rewrote it. I took into account the comments and discussions and wrote as a “celery task” which starts when I upload a photo. As though the general demonization remained, but in other plan.

Source: https://habr.com/ru/post/154567/


All Articles