Тысячи изображений, как мне организовать структуру каталогов? (Linux)

Я получаю тысячи фотографий, загруженных тысячами пользователей на моем Linux-сервере, который размещен на сайте 1and1.com(я считаю, что они используют CentOS, но я не уверен в этой версии). Это вопрос агностики языка, однако для вашей справки я использую PHP.

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

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

Во всяком случае, для изображений с [email protected], я собирался сделать это:

/images/domain.com/user/images...

Является ли это умным, что, если тысячи пользователей скажут "gmail", возможно, я мог бы пойти глубже, вроде этого

/images/domain.com/[first letter of user name]/user/images...

поэтому для [email protected] это будет...

/images/domain.com/m/mike/images...

Это плохой подход? Что делают все остальные? Я не хочу сталкиваться с проблемами со слишком большим количеством каталогов...


по теме:

Ответ 1

Я бы сделал следующее:

  • Возьмите хэш MD5 каждого изображения, когда оно появится.
  • Напишите этот хэш MD5 в базе данных, где вы отслеживаете эти вещи.
  • Сохраните их в структуре каталогов, где вы используете первую пару байтов шестнадцатеричной строки хеша MD5 в качестве имени dir. Поэтому, если хеш "abcdef1234567890", вы сохраните его как "a/b/abcdef1234567890".

Использование хэша также позволяет объединить один и тот же образ, загруженный несколько раз.

Ответ 2

чтобы расширить подход Джо Беды:

  • базы данных
  • базы данных
  • базы данных

если вам нужна группировка или поиск файлов пользователем, исходное имя файла, дата загрузки, дата снятия фотографии (EXIF) и т.д., сохраняйте эти метаданные в базе данных и используйте соответствующие запросы для выбора соответствующих файлов.

Использовать первичный ключ базы данных — будь то хэш файла или автоинкрементный номер — для поиска файлов среди фиксированного набора каталогов (в качестве альтернативы используйте фиксированные максимальные номера файлов N для каждого каталога, а когда вы заполняете переход к следующему, например, k-я фотография должна храниться в {somepath}/aaaaaa/bbbb.jpg, где aaaaaa = floor (k/N), отформатированный как десятичный или шестнадцатеричный, и bbbb = mod (k, N), отформатированный как десятичный или шестнадцатеричный. Если это слишком плоская иерархия для вас, используйте что-то вроде {somepath}/aa/bb/cc/dd/ee.jpg)

Не подвергайте структуру каталогов непосредственно вашим пользователям. Если они используют веб-браузеры для доступа к вашему серверу через HTTP, дайте им URL-адрес, например, www.myserver.com/images/{primary key} и закодируйте правильный тип файла в заголовке Content-Type.

Ответ 3

То, что я использовал для другого требования, но которое может соответствовать вашим потребностям, заключается в использовании простого соглашения.

Приращение на 1 и получение длины нового номера, а затем префикс с этим номером.

Например:

Предположим, что 'a' - это var, который задается с последним id.

a = 564;
++a;
prefix = length(a);
id = prefix + a; // 3565

Затем вы можете использовать временную метку для каталога, используя это соглашение:

20092305 (yyyymmdd)

Затем вы можете взорвать свой путь следующим образом:

2009/23/05/3565.jpg

(или более)

Это интересно, потому что вы можете сохранить порядок сортировки по дате и по числу одновременно (иногда полезно) И вы еще можете разложить свой путь в других каталогах

Ответ 4

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

По сути, идея состоит в том, чтобы использовать последние цифры уникального идентификатора базы данных каждого участника, чтобы вычислить структуру каталогов с уникальным каталогом для всех. Использование последних цифр, а не первого, обеспечивает более равномерное распространение каталогов. Отдельный каталог для каждого члена означает, что задачи обслуживания намного проще, плюс вы можете видеть, где люди (как в визуальном виде). ​​

// checks for member-directories & creates them if required
function member_dirs($user_id) {

    $user_id = sanitize_var($user_id);

    $last_pos = strlen($user_id);
    $dir_1_pos = $last_pos - 1;
    $dir_2_pos = $last_pos - 2;
    $dir_3_pos = $last_pos - 3;

    $dir_1 = substr($user_id, $dir_1_pos, $last_pos);
    $dir_2 = substr($user_id, $dir_2_pos, $last_pos);
    $dir_3 = substr($user_id, $dir_3_pos, $last_pos);

    $user_dir[0] = $GLOBALS['site_path'] . "files/members/" . $dir_1 . "/";
    $user_dir[1] = $user_dir[0] . $dir_2 . "/";
    $user_dir[2] = $user_dir[1] . $dir_3 . "/";
    $user_dir[3] = $user_dir[2] . $user_id . "/";
    $user_dir[4] = $user_dir[3] . "sml/";
    $user_dir[5] = $user_dir[3] . "lrg/";

    foreach ($user_dir as $this_dir) {
        if (!is_dir($this_dir)) { // directory doesn't exist
            if (!mkdir($this_dir, 0777)) { // attempt to make it with read, write, execute permissions
                return false; // bug out if it can't be created
            }
        }
    }

    // if we've got to here all directories exist or have been created so all good
    return true;

}

// accompanying function to above
function make_path_from_id($user_id) {

    $user_id = sanitize_var($user_id);

    $last_pos = strlen($user_id);
    $dir_1_pos = $last_pos - 1;
    $dir_2_pos = $last_pos - 2;
    $dir_3_pos = $last_pos - 3;

    $dir_1 = substr($user_id, $dir_1_pos, $last_pos);
    $dir_2 = substr($user_id, $dir_2_pos, $last_pos);
    $dir_3 = substr($user_id, $dir_3_pos, $last_pos);

    $user_path = "files/members/" . $dir_1 . "/" . $dir_2 . "/" . $dir_3 . "/" . $user_id . "/";
    return $user_path;

}

sanitize_var() является вспомогательной функцией для очистки ввода и обеспечения ее числового значения, $GLOBALS ['site_path'] - это абсолютный путь для сервера. Надеюсь, в противном случае они будут понятны.

Ответ 5

Ответ на Joe Beda почти идеальный, но обратите внимание, что MD5 доказал, что он может быть убит в течение 2 часов на ноутбуке?

Тем не менее, если вы на самом деле будете использовать хэш файла MD5 описанным образом, ваш сервис станет уязвимым для атак. Как будет выглядеть атака?

  • Хакеру не нравится конкретное фото
  • Он гарантирует, что это простой MD5, который вы используете (MD5 изображения + secret_string может напугать его)
  • Он использует волшебный метод столкновения картины (используйте ваше воображение здесь) хеш с фотографией, которую он не любит
  • Он загружает фотографию, как обычно.
  • Ваша служба перезаписывает старую новую и отображает оба

Кто-то говорит: пусть не перезаписывает его тогда. Затем, если можно предсказать, что кто-то выгрузит что-то (например, популярная картинка в Интернете может быть загружена), сначала можно взять "хэш-место". Пользователь был бы счастлив при загрузке изображения котенка, он обнаружил, что он на самом деле выглядит как (используйте свое воображение здесь). Я говорю: используйте SHA1, поскольку было доказано, что он был взломан в течение 127 лет кластером в 10 000 компьютеров?

Ответ 6

Возможно, будет поздно в игре. Но одно решение (если оно подходит вашему прецеденту) может быть хэшированием имени файла. Это способ создания легко воспроизводимого пути к файлу с использованием имени файла, а также создания хорошо распределенной структуры каталогов. Например, вы можете использовать байты файла hashcode имени файла как его путь:

String fileName = "cat.gif";
int hash = fileName.hashCode();
int mask = 255;
int firstDir = hash & mask;
int secondDir = (hash >> 8) & mask;

Это приведет к тому, что путь будет следующим:

/172/029/cat.gif

Затем вы можете найти cat.gif в структуре каталогов, воспроизведя алгоритм.

Использование HEX в качестве имен каталогов будет таким же простым, как преобразование значений int:

String path = new StringBuilder(File.separator)
        .append(String.format("%02x", firstDir))
        .append(File.separator)
        .append(String.format("%02x", secondDir)
        .toString();

Результат:

/AC/1D/cat.gif

Я написал статью об этом несколько лет назад и недавно перенес ее на Medium. Он содержит несколько подробностей и пример кода: Хеширование имен файлов: создание хеш-структуры каталогов. Надеюсь, это поможет!