
Bonne santé, Hawkers! Dans le cadre du travail sur un projet de site de rencontres, il est devenu nécessaire d'organiser le stockage des photos des utilisateurs. Selon les termes de référence, le nombre de photos par utilisateur est limité à 10 fichiers. Mais il peut y avoir des dizaines de milliers d'utilisateurs. Surtout si l'on considère que le projet dans sa forme actuelle existe déjà depuis le début du "zéro". Autrement dit, il y a déjà des milliers d'utilisateurs dans la base de données. À ma connaissance, presque tous les systèmes de fichiers réagissent très négativement à un grand nombre de nœuds enfants dans un dossier. Par expérience, je peux dire que les problèmes commencent après 1000 à 1500 fichiers / dossiers dans le dossier parent.
Clause de non-responsabilité. J'ai cherché sur Google avant d'écrire l'article et j'ai trouvé plusieurs solutions au problème en discussion (par exemple, ici ou ici ). Mais je n'ai pas trouvé une seule solution qui correspond exactement à la mienne. De plus, dans cet article, je ne partage que ma propre expérience dans la résolution du problème.Théorie
En plus de la tâche de stockage en tant que telle, il y avait également une condition dans l'énoncé des travaux, selon laquelle il était nécessaire de laisser des légendes et des légendes pour les photos. Bien sûr, vous ne pouvez pas vous passer d'une base de données. Autrement dit, la première chose que nous faisons est de créer un tableau dans lequel nous prescrivons le mappage des métadonnées (signatures, titres, etc.) avec les fichiers sur le disque. Chaque fichier correspond à une ligne de la base de données. En conséquence, chaque fichier a un identifiant.
Une petite digression. Parlons de l'auto-incrémentation. Un site de rencontres peut compter une douzaine ou deux mille utilisateurs. La question est de savoir combien d'utilisateurs passent généralement par le projet pendant toute la durée de son existence. Par exemple, le public actif de dating-ru est de plusieurs centaines de milliers. Cependant, imaginez simplement combien d'utilisateurs sont restés pendant la durée de vie de ce projet; combien d'utilisateurs ne sont pas activés jusqu'à présent. Maintenant, ajoutons à notre législation, qui nous oblige à stocker des informations sur les utilisateurs pendant au moins six mois ... Tôt ou tard, 4 avec un sou d'
UNSIGNED INT se terminera. Par conséquent, il est préférable de prendre
BIGINT pour la clé primaire.
Essayons maintenant d'imaginer un certain nombre de type
BIGINT . C'est 8 octets. Chaque octet est compris entre 0 et 255. 255 nœuds enfants sont tout à fait normaux pour tout système de fichiers. Autrement dit, nous prenons l'identifiant du fichier en représentation hexadécimale, nous le divisons en morceaux de deux caractères. Nous utilisons ces morceaux comme noms de dossier, ces derniers comme nom de fichier physique. PROFIT!
0f/65/84/10/67/68/19/ff.file
Élégant et simple. L'extension de fichier n'est pas importante ici. Quoi qu'il en soit, le fichier sera fourni par un script qui donnera au navigateur un type MIME particulier, que nous stockerons également dans la base de données. De plus, le stockage d'informations sur le fichier dans la base de données vous permet de redéfinir le chemin d'accès au navigateur. Supposons que le fichier que nous avons se trouve réellement par rapport au répertoire du projet le long du chemin
/content/files/0f/65/84/10/67/68/19/ff.file
. Et dans la base de données, vous pouvez y écrire une URL, par exemple,
/content/users/678/files/somefile
. Les SEO sont probablement assez souriants en ce moment. Tout cela nous permet de ne plus nous soucier de l'emplacement physique du fichier.
Table dans la base de données
En plus de l'identifiant, du type MIME, de l'URL et de l'emplacement physique, nous stockons les fichiers md5 et sha1 dans le tableau pour filtrer les mêmes fichiers si nécessaire. Bien sûr, nous devons également stocker les relations d'entité dans cette table. Supposons l'ID utilisateur auquel appartiennent les fichiers. Et si le projet n'est pas très important, alors dans le même système, nous pouvons stocker, disons, des photographies de marchandises. Par conséquent, nous allons également stocker le nom de la classe d'entité à laquelle appartient l'enregistrement.
En parlant d'oiseaux. Si vous fermez le dossier à l'aide de .htaccess pour un accès externe, le fichier ne peut être obtenu que via le script. Et dans le script, il sera possible de déterminer l'accès au fichier. Pour l'avenir, je dirai que dans mon CMS (où le projet susmentionné est actuellement en cours de sciage), l'accès est déterminé par des groupes d'utilisateurs de base, dont j'ai 8 - invités, utilisateurs, gestionnaires, administrateurs, inactifs, bloqués, supprimés et super-administrateurs. Le super-administrateur peut absolument tout faire, il ne participe donc pas à la détermination de l'accès. Si l'utilisateur a un drapeau de super-administrateur, il est alors super-administrateur. Tout est simple. Autrement dit, nous déterminerons l'accès aux sept groupes restants. L'accès est simple - soit donner le fichier ou ne pas donner. Au total, vous pouvez prendre un champ de type
TINYINT .
Et encore une chose. Selon notre loi, nous devrons stocker physiquement des images personnalisées. Autrement dit, nous devons en quelque sorte marquer les images comme supprimées, au lieu de les supprimer physiquement. Il est plus pratique d'utiliser un champ de bits à ces fins. Dans de tels cas, j'utilise généralement un champ de type
INT . Pour réserver, pour ainsi dire. De plus, j'ai une tradition déjà établie de placer le drapeau
SUPPRIMÉ dans le 5e bit depuis la fin. Mais cela n'a plus d'importance à nouveau.
Qu'avons-nous en conséquence:
create table `files` ( `id` bigint not null auto_increment,
Classe Dispatcher
Maintenant, nous devons créer une classe avec laquelle nous allons télécharger des fichiers. La classe devrait permettre de créer des fichiers, de remplacer / modifier des fichiers, de supprimer des fichiers. En outre, il convient de considérer deux points. Tout d'abord, le projet peut être transféré de serveur en serveur. Donc, dans la classe, vous devez définir une propriété qui contient le répertoire racine des fichiers. Deuxièmement, ce sera très désagréable si quelqu'un frappe une table dans la base de données. Vous devez donc prévoir la possibilité de récupération des données. Avec le premier, tout est généralement clair. Quant à la sauvegarde des données, nous ne réserverons que ce qui ne peut pas être restauré.
ID - restauré à partir de l'emplacement physique du fichier
entity_type - non restauré
entité - non restaurée
mime - restauré à l'aide de l'extension finfo
md5 - est restauré à partir du fichier lui-même
sha1 - restauré à partir du fichier lui-même
fichier - restauré à partir de l'emplacement physique du fichier
URL - non restaurée
méta - non restauré
taille - restaurée à partir du fichier lui-même
créé - vous pouvez prendre des informations à partir d'un fichier
mis Ă jour - vous pouvez extraire des informations d'un fichier
accès - non restauré
drapeaux - non restaurés
Vous pouvez immédiatement supprimer les méta-informations. Il n'est pas essentiel au fonctionnement du système. Et pour une récupération plus rapide, vous devez toujours enregistrer le type MIME. Total: type d'entité, ID d'entité, MIME, URL, accès et indicateurs. Afin d'augmenter la fiabilité du système, nous stockons les informations de sauvegarde pour chaque dossier de destination séparément dans le dossier lui-même.
Code de classe <?php class BigFiles { const FLAG_DELETED = 0x08000000;
Considérez quelques points:
-
realRoot - le chemin complet vers le dossier avec le système de fichiers se terminant par une barre oblique.
-
webRoot - le chemin depuis la racine du site sans barre oblique (voir pourquoi ci-dessous).
- En tant que SGBD, j'utilise l'extension
MySQLi .
- En fait, les informations du tableau
$ _FILES sont passées comme premier argument à la méthode de
téléchargement .
- Si vous appelez la méthode de
mise Ă
jour pour transmettre l'ID d'un fichier existant, elle sera remplacée si le tableau d'entrée dans
tmp_name n'est pas vide.
- Vous pouvez supprimer et modifier les drapeaux de fichiers plusieurs à la fois. Pour ce faire, au lieu de passer l'identifiant du fichier, vous devez passer soit un tableau avec des identifiants, soit une chaîne avec eux, séparés par des virgules.
Acheminement
En fait, cela se résume à quelques lignes dans htaccess à la racine du site (on suppose que mod_rewrite est activé):
RewriteCond %{REQUEST_URI} ^/content/(.*)$ RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.+)$ content/index.php?file=$1 [L,QSA]
«Contenu» est le dossier à la racine du site dans mon cas. Bien sûr, vous pouvez nommer le dossier différemment. Et bien sûr index.php lui-même, stocké dans mon cas dans le dossier de contenu:
<?php $dbHost = '127.0.0.1'; $dbUser = 'user'; $dbPass = '****'; $dbName = 'database'; try { if (empty($_REQUEST['file'])) { header('HTTP/1.1 400 Bad Request'); exit; } $userG = 'anonimous';
Eh bien, en soi, nous fermons le système de fichiers lui-même de l'accès externe. Placez le fichier
.htaccess
Ă la racine du dossier
content/files
avec une seule ligne:
Deny from all
Résumé
Cette solution évite la perte de performances du système de fichiers en raison d'une augmentation du nombre de fichiers. Au moins, les problèmes sous la forme de milliers de fichiers dans un dossier peuvent certainement être évités. Et en même temps, nous pouvons organiser et contrôler l'accès aux fichiers à des adresses lisibles par l'homme. De plus, le respect de notre sombre législation. Faites immédiatement une réservation, cette solution n'est PAS un moyen complet de protéger le contenu. N'oubliez pas: si quelque chose joue dans le navigateur, il peut être téléchargé gratuitement.