Стратегии хранения изображений

Когда пользователь загружает изображение на мой сайт, изображение проходит через этот процесс;

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

Пока сайт довольно маленький, и в каталоге uploads есть только ~ 200 000 изображений. Я понимаю, что я нигде не ограничен физическим пределом файлов в каталоге, но этот подход явно не будет масштабироваться, поэтому мне было интересно, есть ли у кого-нибудь советы по стратегиям загрузки и хранения для обработки больших объемов загрузки изображений.

EDIT: Создание вложенных папок имени пользователя (или, более конкретно, userid) будет хорошим решением. С немного больше копания, я нашел отличную информацию прямо здесь; Как хранить изображения в вашей файловой системе
Тем не менее, будет ли этот подход с использованием idid хорошо масштабироваться, если CDN будет куплен в уравнение?

Solutions Collecting From Web of "Стратегии хранения изображений"

Я ответил на аналогичный вопрос раньше, но я не могу его найти, возможно, OP удалил его вопрос …

В любом случае, решение Adams кажется лучшим до сих пор, но оно не является пуленепробиваемым, так как images/c/cf/ (или любая другая пара dir / subdir) может содержать до 16 ^ 30 уникальных хэшей и по крайней мере в 3 раза больше файлы, если мы подсчитываем расширения изображений, намного больше, чем может обрабатывать любая обычная файловая система.

AFAIK, SourceForge.net также использует эту систему для репозиториев проектов, например, проект «без жира» будет размещен в projects/f/fa/fatfree/ , однако я считаю, что они ограничивают названия проектов 8 символами.


Я бы сохранил хэш изображения в базе данных вместе с полем DATE / DATETIME / TIMESTAMP указывающим, когда изображение было загружено / обработано, а затем поместите изображение в структуру следующим образом:

 images/ 2010/ - Year 04/ - Month 19/ - Day 231c2ee287d639adda1cdb44c189ae93.png - Image Hash 

Или:

 images/ 2010/ - Year 0419/ - Month & Day (12 * 31 = 372) 231c2ee287d639adda1cdb44c189ae93.png - Image Hash 

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

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

Конечно, если этого недостаточно для вас, вы всегда можете добавить дополнительные субдиры (часы, минуты, …).

Лично я бы не использовал идентификаторы пользователей, если у вас нет этой информации, доступной в вашей базе данных, потому что:

  1. Раскрытие имен пользователей в URL-адресе
  2. Имена пользователей нестабильны (вы можете переименовывать папки, но все же …)
  3. Пользователь может гипотетически загрузить большое количество изображений
  4. Не имеет цели (?)

Что касается CDN, я не вижу никакой причины, по которой эта схема (или любая другая) не будет работать …

MediaWiki генерирует сумму MD5 имени загруженного файла и использует первые две буквы MD5 (скажем, «c» и «f» суммы «cf1e66b77918167a6b6b972c12b1c00d») для создания этой структуры каталогов:

 images/c/cf/Whatever_filename.png 

Вы также можете использовать идентификатор изображения для прогнозируемого верхнего предела количества файлов в каталоге. Возможно, floor(image unique ID / 1000) чтобы определить родительский каталог, для 1000 изображений в каталоге.

Думали ли вы использовать что-то вроде Amazon S3 для хранения файлов? Я запускаю фотохостинг-компанию и, быстро достигнув ограничений на нашем собственном сервере, перешел на AmazonS3. Красота S3 заключается в том, что нет никаких ограничений, таких как inodes, а что нет, вы просто продолжаете бросать в него файлы.

Кроме того: если вам не нравится S3, вы всегда можете попробовать и разбить его на подпапки настолько, насколько сможете:

/userid/year/month/day/photoid.jpg

Вы можете преобразовать имя пользователя в md5 и установить папку из 2-3 первых букв md5, преобразованного имени пользователя для аватаров, и для изображений, которые вы можете конвертировать и воспроизводить со временем, случайных строк, идентификаторов и имен

8648b8f3ce06a7cc57cf6fb931c91c55 – devcline

Также первая буква имени пользователя или идентификатора для следующей папки или обратной

Это будет выглядеть

Состав:

 stream/img/86/8b8f3ce06a7cc57cf6fb931c91c55.png //simplest stream/img/d/2/0bbb630d63262dd66d2fdde8661a410075.png //first letter and id folders stream/img/864/d/8b8f3ce06a7cc57cf6fb931c91c55.png // with first letter of the nick stream/img/864/2/8b8f3ce06a7cc57cf6fb931c91c55.png //with unique id stream/img/2864/8b8f3ce06a7cc57cf6fb931c91c55.png //with unique id in 3 letters stream/img/864/2_8b8f3ce06a7cc57cf6fb931c91c55.png //with unique id in picture name 

Код

 $username = substr($username_md5, 1); // to cut first letter from the md5 converted nick $username_first = $username[0]; // the first letter $username_md5 = md5($username); // md5 for username $randomname = uniqid($userid).md5(time()); //for generate a random name based on ID 

вы можете попробовать также с base64

  $image_encode = strtr(base64_encode($imagename), '+/=', '-_,'); $image_decode = base64_decode(strtr($imagename, '-_,', '+/=')); 

Steam И dokuwiki используют эту структуру.

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

Идея использования хэша md5 – лучший способ обработки массивного хранилища изображений. Помня о том, что разные значения могут иметь один и тот же хеш, я настоятельно рекомендую добавить также идентификатор пользователя или nicname в путь, чтобы сделать его уникальным. Да, это все, что нужно. Если у кого-то разные пользователи с одинаковым идентификатором базы данных – ну, что-то не так;) Итак, root_path/md5_hash/user_id – это все, что вам нужно, чтобы сделать это правильно.

Использование DATE / DATETIME / TIMESTAMP не является оптимальным решением по способу ИМО. В конечном итоге вы получаете большие скопления папок с изображениями в день создания и почти пустые на менее посещаемых. Не уверен, что это приводит к проблемам производительности, но есть что-то вроде эстетики данных, и последовательное распределение данных всегда превосходит.

Таким образом, я явно ища хэш-решение. введите описание изображения здесь

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

 /** * Generates directory path using $user_id md5 hash for massive image storing * @author Hexodus * @param string $user_id numeric user id * @param string $user_root_raw root directory string * @return null|string */ function getUserImagePath($user_id = null, $user_root_raw = "images/users", $padding_length = 16, $split_length = 3, $hash_length = 12, $hide_leftover = true) { // our db user_id should be nummeric if (!is_numeric($user_id)) return null; // clean trailing slashes $user_root_rtrim = rtrim( $user_root_raw, '/\\' ); $user_root_ltrim = ltrim( $user_root_rtrim, '/\\' ); $user_root = $user_root_ltrim; $user_id_padded = str_pad($user_id, $padding_length, "0", STR_PAD_LEFT); //pad it with zeros $user_hash = md5($user_id); // build md5 hash $user_hash_partial = $hash_length >=1 && $hash_length < 32 ? substr($user_hash, 0, $hash_length) : $user_hash; $user_hash_leftover = $user_hash_partial <= 32 ? substr($user_hash, $hash_length, 32) : null; $user_hash_splitted = str_split($user_hash_partial, $split_length); //split in chunks $user_hash_imploded = implode($user_hash_splitted,"/"); //glue aray chunks with slashes if ($hide_leftover || !$user_hash_leftover) $user_image_path = "{$user_root}/{$user_hash_imploded}/{$user_id_padded}"; //build final path else $user_image_path = "{$user_root}/{$user_hash_imploded}/{$user_hash_leftover}/{$user_id_padded}"; //build final path plus leftover return $user_image_path; } 

Функциональные тестовые вызовы:

 $user_id = "1394"; $user_root = "images/users"; $user_hash = md5($user_id); $path_sample_basic = getUserImagePath($user_id); $path_sample_advanced = getUserImagePath($user_id, "images/users", 8, 4, 12, false); echo "<pre>hash: {$user_hash}</pre>"; echo "<pre>basic:<br>{$path_sample_basic}</pre>"; echo "<pre>customized:<br>{$path_sample_advanced}</pre>"; echo "<br><br>"; 

Полученный результат – раскрашен для вашего удобства;): введите описание изображения здесь

Вы можете рассмотреть open source http://danga.com/mogilefs/, поскольку он идеально подходит для того, что вы делаете. Это заставит вас подумать о папках в пространствах имен (которые могут быть пользователями) и позволить им хранить ваши изображения для вас. Лучшая часть – вам не нужно заботиться о том, как хранятся данные. Это делает его полностью избыточным, и вы даже можете установить элементы управления, как избыточные эскизы.

Я долгое время пользовался имением. Это довольно старый код и может быть дополнительно оптимизирован, но он все еще хорош, как есть.

Это неизменная функция, создающая структуру каталогов на основе:

  1. Номер, идентифицирующий изображение (FILE ID):

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

  1. Базовый каталог

  2. Максимальное количество файлов и поддиректории первого уровня. Это обещание можно сохранить, только если каждый идентификатор FILE уникален.

Пример использования:

Использование явно FILE ID:

 $fileName = 'my_image_05464hdfgf.jpg'; $fileId = 65347; $baseDir = '/home/my_site/www/images/'; $baseURL = 'http://my_site.com/images/'; $clusteredDir = \DirCluster::getClusterDir( $fileId ); $targetDir = $baseDir . $clusteredDir; $targetPath = $targetDir . $fileName; $targetURL = $baseURL . $clusteredDir . $fileName; 

Использование имени файла, number = crc32 (имя файла)

 $fileName = 'my_image_05464hdfgf.jpg'; $baseDir = '/home/my_site/www/images/'; $baseURL = 'http://my_site.com/images/'; $clusteredDir = \DirCluster::getClusterDir( $fileName ); $targetDir = $baseDir . $clusteredDir; $targetURL = $baseURL . $clusteredDir . $fileName; 

Код:

 class DirCluster { /** * @param mixed $fileId - numeric FILE ID or file name * @param int $maxFiles - max files in one dir * @param int $maxDirs - max 1st lvl subdirs in one dir * @param boolean $createDirs - create dirs? * @param string $path - base path used when creatign dirs * @return boolean|string */ public static function getClusterDir($fileId, $maxFiles = 100, $maxDirs = 10, $createDirs = false, $path = "") { // Value for return $rt = ''; // If $fileId is not numerci - lets create crc32 if (!is_numeric($fileId)) { $fileId = crc32($fileId); } if ($fileId < 0) { $fileId = abs($fileId); } if ($createDirs) { if (!file_exists($path)) { // Check out the rights - 0775 may be not the best for you if (!mkdir($path, 0775)) { return false; } @chmod($path, 0775); } } if ( $fileId <= 0 || $fileId <= $maxFiles ) { return $rt; } // Rest from dividing $restId = $fileId%$maxFiles; $formattedFileId = $fileId - $restId; // How many directories is needed to place file $howMuchDirs = $formattedFileId / $maxFiles; while ($howMuchDirs > $maxDirs) { $r = $howMuchDirs%$maxDirs; $howMuchDirs -= $r; $howMuchDirs = $howMuchDirs/$maxDirs; $rt .= $r . '/'; // DIRECTORY_SEPARATOR = / if ($createDirs) { $prt = $path.$rt; if (!file_exists($prt)) { mkdir($prt); @chmod($prt, 0775); } } } $rt .= $howMuchDirs-1; if ($createDirs) { $prt = $path.$rt; if (!file_exists($prt)) { mkdir($prt); @chmod($prt, 0775); } } $rt .= '/'; // DIRECTORY_SEPARATOR return $rt; } }