За последние 24 часа нас посетил 20181 программист и 1004 робота. Сейчас ищут 287 программистов ...

Синхронизация каталогов : алгоритм

Тема в разделе "PHP для новичков", создана пользователем twim32, 17 фев 2020.

  1. twim32

    twim32 Активный пользователь

    С нами с:
    29 мар 2017
    Сообщения:
    275
    Симпатии:
    58
    Заказчик заливает фаилы через фтп, нужно сделать скрипт который генерирует фотографии (в разных форматах, с водяным знаком и без).

    Но вот нельзя просто взять и синхронизировать каталоги (мем.jpg).

    Условия:
    1. Имена субдиректорий и фаилов, так же как и структуру папок, в source нельзя менять.
    2. В одной субпапке sourc'a может быть от 1 до 40 фотографий маленьких и средних размеров
    3. Папок destination - 5 штук, но для простоты я нарисовал только 2.
    4. Папки и фаилы в destination обязательно должны быть переименованы, так как в папке source могут иметь слишком длинные имена. А PHP не может удалять фаилы с слишком длинными именами... Да и вообще, есть еще пара причин.
    5. Если были добавлены/удалены/изменены фотографии в субдиректориях, то изменения должны коснуться субдиректорий destination. (спс, кэп!). Причём при изменении фотографии, ее имя должно поменяться для папок destination.
    6. Порядок фотографий всегда должен быть идентичным.

    [source]
    ----[a, b, c]
    --------a, b, c-1.jpg
    --------a, b, c-2.jpg
    ----[q, e, w]
    --------q, e, w-1.jpg
    --------q, e, w-2.jpg
    ----[r, w, q]
    --------r, w, q-1.jpg
    --------r, w, q-2.jpg
    [destination-1]
    ----[a_b_c]
    --------1_(md5_file).jpg
    --------2_(md5_file).jpg
    ----[q_e_w]
    --------1_(md5_file).jpg
    --------2_(md5_file).jpg
    ----[r_w_q]
    --------1_(md5_file).jpg
    --------2_(md5_file).jpg
    [destination-2]
    ----[a_b_c]
    --------1_(md5_file).jpg
    --------2_(md5_file).jpg
    ----[q_e_w]
    --------1_(md5_file).jpg
    --------2_(md5_file).jpg
    ----[r_w_q]
    --------1_(md5_file).jpg
    --------2_(md5_file).jpg


    Вот моё решение:
    1. Генерируем список фаилов source с md5_file в таком формате:
    [
    ----[a, b, c] => [
    --------a, b, c-1.jpg => md5_file
    --------a, b, c-2.jpg => md5_file
    --------],

    ----[q, e, w] => [
    --------q, e, w-1.jpg => md5_file
    --------q, e, w-2.jpg => md5_file
    --------],

    ----[r, w, q] => [
    --------r, w, q-1.jpg => md5_file
    --------r, w, q-2.jpg => md5_file
    --------],
    ]

    2. Загружаем результаты старого скана из фаила (у меня Json)
    3. Ищем папки которые были удалены, и папки которые были изменены. Удаляем и те и другие.
    4. Формируем список фаилов которые должны быть сгенерированы.
    5. С помощью ajax генерируем фаилы в destination's, проверяя существует ли папка с новым именем.
    6. Если всё прошло удачно, сохраняю новый список в фаил.

    Что меня не устраивает, так это при изменении одной фотографии, все фаилы в папке будут удалены и сгенерированы заного. Но иначе я не знаю как сохранить порядок фотографий...

    Вопрос: может быть у вас есть решение поэлегантней? где ненужно перелопачивать всю папку ради одной изменённой фотки...
     
    #1 twim32, 17 фев 2020
    Последнее редактирование: 17 фев 2020
  2. twim32

    twim32 Активный пользователь

    С нами с:
    29 мар 2017
    Сообщения:
    275
    Симпатии:
    58
    Забыл сказать: загрузка фаилов должна быть только через фтп. Заказчик не желает загружать каждую субпапку отдельным архивом через форму. Он согласен только на загрузку всей папки source архивом, но ясно-понятно что ни один малобюджетный хостинг нам этого не предоставит.
     
  3. MouseZver

    MouseZver Суперстар

    С нами с:
    1 апр 2013
    Сообщения:
    7.748
    Симпатии:
    1.321
    Адрес:
    Лень
    даже zip архив ?
     
  4. ADSoft

    ADSoft Старожил

    С нами с:
    12 мар 2007
    Сообщения:
    3.822
    Симпатии:
    736
    Адрес:
    Татарстан
    вообще это о чем... что за порядок файлов? порядок - он как тебе надо - так и будет - в зависимости от того какая сортировка задана
     
  5. twim32

    twim32 Активный пользователь

    С нами с:
    29 мар 2017
    Сообщения:
    275
    Симпатии:
    58
    Даже zip архив. Папок много и если ему вздумается поменять несколько, то ему лень вспоминать какие он менял, а какие - нет. Он согласен только на весь каталог сразу, но я пологаю что хостинг не позволит так как сжатый на ультрах весит 700+ мб
    Тут дело не в сортировке массива фотографий, а в том, что каждый раз когда он делает манипуляции с фотографиями в субпапке, он обновляет имена.

    Например, массив фотографий до изменений: [photo1, photo2, photo3, photo4]. Он удалил photo2 и поменял контраст в photo4 и добавил еще одну фотку в конец массива. После этого он каждый раз переименовывает все фотки : [photo1, photo2(ранее photo3), photo3 (ранее photo4), photo4(новая)]. Моя задача (в идеале) определить что фото2 была удалена и удалить ее из других каталогов, и обновить photo3 и переименовать его из 3-{md5_file() старого фаила} в 3-{md5_file() изменённого} и создать photo4 c именем 4-{md5_file() источника}. Но на данный момент я просто определяю что были изменения в папке источника, удаляю папку из каталогов и генерирую ее снова.

    Надеюсь, мой пример прост в понимании.
     
  6. ADSoft

    ADSoft Старожил

    С нами с:
    12 мар 2007
    Сообщения:
    3.822
    Симпатии:
    736
    Адрес:
    Татарстан
    имхо проще пересмотреть все с нуля - в частности ограничения все по задаче
     
  7. twim32

    twim32 Активный пользователь

    С нами с:
    29 мар 2017
    Сообщения:
    275
    Симпатии:
    58
    Я полностью поддерживаю ваше мнения и с удовольствием бы переписал всё, но это уже совсем другая история.

    Спасибо Вам за потраченное время, пойду дальше собирать Франкенштейна.
     
  8. виталий032

    виталий032 Активный пользователь

    С нами с:
    31 янв 2014
    Сообщения:
    227
    Симпатии:
    30
    Адрес:
    Владивосток
    Учитывая данное условие:
    Код (Text):
    1. 1. Имена субдиректорий и фаилов, так же как и структуру папок, в source нельзя менять.
    Можно предложить следующее решение:
    1. Выявить файлы, которые были изменены (по содержанию), удалены, созданы.
    2. Сделать соответствующие операции по синхронизации с папкой destination-*


    Функция findFilesThatWereChanged() вызывается из функции synchronizeFiles().
    Могут быть ошибки в синтаксисе, так как было противостояние js, c# и php.

    PHP:
    1. function findFilesThatWereChanged($oldTree, $newTree) {
    2.   $changes = [
    3.     'created' => [],
    4.     'removed' => [],
    5.     'changed' => []
    6.   ];
    7.  
    8.   foreach($newTree as $subfolder_name => $subfolder_files) {
    9.     // так как мы точно знаем, что папка [a,b,c] будет существовать, то идем дальше
    10.     foreach($subfolder_files as $filename => $fileMd5) {
    11.       // ловим те файлы, которые созданы
    12.       if (!array_key_exists($filename, $oldTree[$subfolder_name])) {
    13.         $changes['created'].push(
    14.           [
    15.             $filename => [
    16.               'subfolder' => $subfolder_name,
    17.               'md5' => $fileMd5,
    18.             ]
    19.           ]);
    20.       }
    21.  
    22.       $md5Old = $oldTree[$subfolder_name][$filename];
    23.       $md5New = $newTree[$subfolder_name][$filename];
    24.  
    25.       // ловим те файлы, которые изменены
    26.       if (array_key_exists($fileName, $oldTree[$subfolder_name]) &&
    27.         $md5Old != $md5New) {
    28.         $changes['changed'].push(
    29.           [
    30.             $filename => [
    31.               'subfolder' => $subfolder_name,
    32.               'md5' => $fileMd5,
    33.             ]
    34.           ]);
    35.       }
    36.     }
    37.   }
    38.  
    39.   // чтобы узнать  какие файлы были удалены найдем имена (ключи), которых нет в новом дереве
    40.   foreach($oldTree as $subfolder_name => $subfolder_files) {
    41.     // так как мы точно знаем, что папка [a,b,c] будет существовать, то идем дальше
    42.     foreach($subfolder_files as $filename => $fileMd5) {
    43.       if (!array_key_exists($filename, $newTree[$subfolder_name])) {
    44.         $changes['removed'].push(
    45.           [
    46.             $filename => [
    47.               'subfolder' => $subfolder_name,
    48.               'md5' => $fileMd5,
    49.             ]
    50.           ]);
    51.       }
    52.     }
    53.   }
    54.   return $changes;
    55. }
    56.  
    57. function synchronizeFiles() {
    58.   const SOURCE_FOLDER = __DIR__. '/source';
    59.   const DESTINATIONS_AMOUNT = 5;
    60.   const DESTINATION_FOLDER_PREFIX = 'destination';
    61.  
    62.   // генерируем названия папок destination
    63.   $destination_folders = [];
    64.   for ($i = 1; $i <= 5; $i++) {
    65.     $destination_folders.push(DESTINATION_FOLDER_PREFIX. '-' . $i);
    66.   }
    67.  
    68.   // $oldTree и $newTree это представления папки source
    69.   // массив сгенерированный в прошлый раз, который сейчас находится в определенном файле .json
    70.   $oldTree = [
    71.     ['a, b, c'] => [
    72.       'сamera-canon-g245.jpg' => 'md5-9000'
    73.     ]
    74.   ];
    75.   // массив, который сейчас сгенерировали
    76.   $newTree = [
    77.     ['a, b, c'] => [
    78.       'сamera-canon-g245.jpg' => 'md5-9999'
    79.     ]
    80.   ];
    81.  
    82.   $changes = findFilesThatWereChanged($oldTree, $newTree);
    83.  
    84.   // теперь мы знаем какие файлы удалены, добавлены и изменены
    85.  
    86.   // синхронизируем с destination, итерируем каждый затронутый файл
    87.   foreach($changes['changed'] as $filename => $fileInfo) {
    88.  
    89.     // ищем старую версию файла в destination-* папках
    90.     foreach($destination_folders as $key => $destination_folder) {
    91.       $destination = __DIR__. '/' . $destination_folder;
    92.       // проверяем, что такая папка destination-* существует
    93.       if (is_dir($destination)) {
    94.         // 1_старый-md5.jpg
    95.         $filenameRegex = "*_".$oldTree[$fileInfo['subfolder']][$filename];
    96.         // возвращается массив имен файлов, которые удовлетворяют регулярному выражению,
    97.         // однако в нашем случае оно должно возвратить массив с одним именем файла,
    98.         // так как такой файл по идее должен быть один.
    99.         $file_names = glob($destination. "/". $fileInfo['subfolder'] . '/' . $filenameRegex);
    100.  
    101.         // если в данной папке destination-*/a,b,c файл найден, то ...
    102.         if (!empty($file_names)) {
    103.           // старый файл
    104.           $old_md5_filename = $file_names[0];
    105.           // удаляем старый файл
    106.           unlink($destination. "/" . $fileInfo['subfolder'] . '/' . $old_md5_filename);
    107.  
    108.           // где-то, как-то клеим водяной знак
    109.           // и сохраняем по этому же пути с новым именем файла
    110.  
    111.           // а предварательно получим имя нового файла
    112.           // получим порядковый номер фото 2_md5hash.jpg
    113.           $number = explode('_', $old_md5_filename)[0];
    114.  
    115.           // предположим, что для водяного знака нужно вызвать метод watermark($filename)
    116.           // не забываем, что имя файла и поддиректория не изменны, тогда
    117.           $newFile = watermark(SOURCE_FOLDER . '/' . $fileInfo['subfolder'] . '/' . $filename);
    118.           // генерируем новое название файла
    119.           // с расширением что-то не стал заморачиваться
    120.           $newFileName = $number. "_" . md5($newFile) . ".jpg";
    121.           // сохраняем файл в destination-*
    122.           file_put_contents($destination. "/".$fileInfo['subfolder']. '/'.$newFileName);
    123.           break;
    124.         }
    125.       }
    126.     }
    127.   }
    128.  
    129.   foreach($changes['delete'] as $filename => $fileInfo) {
    130.     // находим в destinations и просто удаляем
    131.   }
    132.  
    133.   foreach($changes['created'] as $filename => $fileInfo) {
    134.     // в какой из destinations добавлять файл я не знаю.
    135.     // Может если в destination-1/a,b,c уже например > 100 файлов, тогда
    136.     // проверять destination-2/a,b,c
    137.   }
    138.  
    139.   // сохраняем новое дерево в .json файл
    140.   file_put_contents(__DIR__. '/sourceFilesTree.json', $newTree);
    141. }
     
    #8 виталий032, 22 фев 2020
    Последнее редактирование: 22 фев 2020
    twim32 нравится это.
  9. twim32

    twim32 Активный пользователь

    С нами с:
    29 мар 2017
    Сообщения:
    275
    Симпатии:
    58
    @виталий032, громнейшее спасибо! Правда я уже сделал рабочий костыль и проект уже на стадии тестирования, но мне за него стыдно. Поэтому при рефакторинге заменю свой код вашим, так как это именно то, что нужно! Еще раз спасибо!