Des milliers d’images, comment organiser la structure des répertoires? (linux)

Je reçois des milliers de photos téléchargées par des milliers d’utilisateurs sur mon serveur Linux, hébergé par 1and1.com (je crois qu’ils utilisent CentOS, mais je ne suis pas sûr de la version). Ceci est une question agnostique de langue, cependant, pour votre référence, j’utilise PHP.

Ma première pensée a été de simplement les vider dans le même répertoire. Cependant, je me souviens tout à l’heure, il y avait une limite au nombre de fichiers ou de répertoires pouvant être déposés dans un répertoire.

Ma seconde pensée était de partitionner les fichiers à l’intérieur des répertoires en fonction de l’adresse e-mail des utilisateurs (car c’est ce que j’utilise pour le nom d’utilisateur de toute façon) mais je ne veux pas courir la limite pour les répertoires dans un répertoire ….

Quoi qu’il en soit, pour les images de [email protected], j’allais faire ceci:

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

Est-ce intelligent de faire, si des milliers d’utilisateurs ont dit “gmail” peut-être que je pourrais même aller plus loin, comme ça

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

donc pour [email protected] ce serait …

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

Est-ce une mauvaise approche? Que font les autres? Je ne veux pas rencontrer de problèmes avec trop de répertoires aussi …


En relation:

  • Combien de fichiers dans un répertoire sont trop nombreux?
  • Structure de dossier Web optimale pour ~ 250 000 images
  • Comment stocker des images dans votre système de fichiers
  • Astuces pour gérer un grand nombre de fichiers?

Je ferais ce qui suit:

  1. Prenez un hash MD5 de chaque image au fur et à mesure de son entrée.
  2. Ecrivez ce hash MD5 dans la firebase database où vous gardez une trace de ces choses.
  3. Stockez-les dans une structure de répertoires où vous utiliserez le premier couple d’octets de la chaîne hexadécimale de hachage MD5 comme nom de répertoire. Donc, si le hachage est “abcdef1234567890”, vous le stockeriez sous le nom “a / b / abcdef1234567890”.

L’utilisation d’un hachage vous permet également de fusionner la même image téléchargée plusieurs fois.

développer l’approche de Joe Beda:

  • firebase database
  • firebase database
  • firebase database

Si vous souhaitez regrouper ou rechercher des fichiers par utilisateur, nom de fichier d’origine, date de téléchargement, date de prise de vue (EXIF), etc., stockez ces métadonnées dans une firebase database et utilisez les requêtes appropriées pour sélectionner les fichiers appropriés.

Utilisez la clé primaire de la firebase database – que ce soit un hachage de fichier ou un numéro auto-incrémenté – pour localiser des fichiers parmi un ensemble fixe de répertoires (utilisez un nombre maximal de fichiers par répertoire et, lorsque vous faites le ensuite, par exemple, la {somepath}/aaaaaa/bbbb.jpg photo doit être stockée dans {somepath}/aaaaaa/bbbb.jpg où aaaaaa = étage (k / N), format décimal ou hexadécimal, et bbbb = mod (k, N), formaté en décimal ou hex. Si c’est une hiérarchie trop plate pour vous, utilisez quelque chose comme {somepath}/aa/bb/cc/dd/ee.jpg )

N’exposez pas la structure de répertoire directement à vos utilisateurs. S’ils utilisent des navigateurs Web pour accéder à votre serveur via HTTP, donnez-leur une URL telle que http://www.myserver.com/images/{primary key} et encodez le bon type de fichier dans l’en-tête Content-Type.

Voici deux fonctions que j’ai écrites il y a quelque temps pour exactement cette situation. Ils sont utilisés depuis plus d’un an sur un site comptant des milliers de membres, chacun ayant beaucoup de fichiers.

Pour l’essentiel, l’idée est d’utiliser les derniers chiffres de l’ID de firebase database unique de chaque membre pour calculer une structure de répertoire, avec un répertoire unique pour tout le monde. L’utilisation des derniers chiffres, plutôt que du premier, garantit une répartition plus homogène des répertoires. Un répertoire distinct pour chaque membre signifie que les tâches de maintenance sont beaucoup plus simples. De plus, vous pouvez voir où se trouvent les objects des personnes (comme visuellement).

 // 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 () est une fonction de prise en charge permettant de nettoyer les entrées et de les rendre numériques, $ GLOBALS [‘site_path’] est le chemin absolu du serveur. Espérons qu’ils s’expliqueront autrement.

Ce que j’ai utilisé pour une autre exigence mais qui peut répondre à vos besoins est d’utiliser une convention simple.

Incrémentez de 1 et obtenez la longueur du nouveau numéro, puis préfixez ce nombre.

Par exemple:

Supposons que “a” est une variable définie avec le dernier identifiant.

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

Ensuite, vous pouvez utiliser un horodatage pour le répertoire, en utilisant cette convention:

 20092305 (yyyymmdd) 

Ensuite, vous pouvez exploser votre chemin comme ceci:

 2009/23/05/3565.jpg 

(ou plus)

C’est intéressant parce que vous pouvez garder un ordre de sorting par date et par numéro en même temps (parfois utile) Et vous pouvez toujours décomposer votre chemin dans plusieurs répertoires

La réponse de Joe Beda est presque parfaite, mais veuillez noter que le MD5 s’est révélé être collisible en 2 heures sur un ordinateur portable?

Cela dit, si vous utilisez réellement le hachage MD5 du fichier de la manière décrite, votre service deviendra vulnérable aux attaques. A quoi ressemblera l’attaque?

  1. Un pirate n’aime pas une photo particulière
  2. Il s’assure qu’il s’agit bien de MD5 que vous utilisez (MD5 de l’image + secret_ssortingng peut le faire fuir)
  3. Il utilise une méthode magique pour entrer en collision avec une photo de (utilisez Votre imagination ici) avec la photo qu’il n’aime pas
  4. Il télécharge la photo comme il le ferait normalement
  5. Votre service écrase l’ancien avec le nouveau et affiche les deux

Quelqu’un dit: ne l’écrasons pas alors. Ensuite, s’il est possible de prédire que quelqu’un va télécharger quelque chose (par exemple, une image populaire sur le Web peut être téléchargée), il est possible d’en prendre le “premier”. L’utilisateur serait heureux en téléchargeant une image d’un chaton, il trouverait que cela apparaît réellement comme (utilisez votre imagination ici). Je dis: utilisez SHA1, car il a été prouvé être piratable dans 127 ans par un cluster de 10.000 ordinateurs?

Pourrait être en retard au jeu à ce sujet. Mais une solution (si elle correspond à votre cas d’utilisation) pourrait être un hachage de nom de fichier. C’est un moyen de créer un chemin de fichier facilement reproductible en utilisant le nom du fichier tout en créant une structure de répertoires bien dissortingbuée. Par exemple, vous pouvez utiliser les octets du hashcode du nom de fichier comme chemin:

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

Cela se traduirait par le chemin:

 /172/029/cat.gif 

Vous pouvez ensuite trouver cat.gif dans la structure des répertoires en reproduisant l’algorithme.

Utiliser HEX comme noms de répertoire serait aussi simple que de convertir les valeurs int :

 Ssortingng path = new SsortingngBuilder(File.separator) .append(Ssortingng.format("%02x", firstDir)) .append(File.separator) .append(Ssortingng.format("%02x", secondDir) .toSsortingng(); 

Résultant en:

 /AC/1D/cat.gif 

J’ai écrit un article à ce sujet il y a quelques années et je l’ai récemment transféré sur Medium. Il contient quelques détails et quelques exemples de code: Hashing des noms de fichiers: création d’une structure de répertoire hachée . J’espère que cela t’aides!