
Gute Gesundheit, Hawkers! Während der Arbeit an einem Dating-Site-Projekt wurde es notwendig, die Speicherung von Benutzerfotos zu organisieren. Gemäß der Leistungsbeschreibung ist die Anzahl der Fotos pro Benutzer auf 10 Dateien begrenzt. Aber es kann Zehntausende von Benutzern geben. Besonders wenn man bedenkt, dass das Projekt in seiner jetzigen Form bereits ab dem Beginn der "Null" existiert. Das heißt, die Datenbank enthält bereits Tausende von Benutzern. Soweit ich weiß, reagiert fast jedes Dateisystem sehr negativ auf eine große Anzahl von untergeordneten Knoten in einem Ordner. Aus Erfahrung kann ich sagen, dass Probleme nach 1000-1500 Dateien / Ordnern im übergeordneten Ordner beginnen.
Haftungsausschluss. Ich habe vor dem Schreiben des Artikels gegoogelt und verschiedene Lösungen für das zur Diskussion stehende Problem gefunden (zum Beispiel hier oder hier ). Aber ich habe keine einzige Lösung gefunden, die genau zu meiner passt. Außerdem teile ich in diesem Artikel nur meine eigenen Erfahrungen bei der Lösung des Problems.Theorie
Neben der Speicheraufgabe als solche gab es in der Arbeitserklärung auch eine Bedingung, wonach Bildunterschriften und Bildunterschriften für Fotos hinterlassen werden mussten. Natürlich können Sie nicht auf eine Datenbank verzichten. Das heißt, wir erstellen zunächst eine Tabelle, in die wir die Metadatenzuordnung (Signaturen, Titel usw.) mit den Dateien auf der Festplatte schreiben. Jede Datei entspricht einer Zeile in der Datenbank. Dementsprechend hat jede Datei eine Kennung.
Ein kleiner Exkurs. Lassen Sie uns über das automatische Inkrementieren sprechen. Eine Dating-Site kann ein Dutzend oder zweitausend Benutzer haben. Die Frage ist, wie viele Benutzer das Projekt im Allgemeinen während der gesamten Dauer seines Bestehens durchlaufen. Zum Beispiel ist das aktive Publikum von dating-ru mehrere hunderttausend. Stellen Sie sich jedoch vor, wie viele Benutzer während der Laufzeit dieses Projekts noch übrig waren. Wie viele Benutzer sind bisher nicht aktiviert? Und jetzt fügen Sie unsere Gesetzgebung hinzu, die uns verpflichtet, Informationen über Benutzer mindestens sechs Monate lang zu speichern ... Früher oder später endet 4 mit einem Cent
UNSIGNED INT . Daher ist es am besten,
BIGINT als Primärschlüssel zu verwenden.
Versuchen wir nun, uns eine Reihe vom Typ
BIGINT vorzustellen . Das sind 8 Bytes. Jedes Byte reicht von 0 bis 255. 255 untergeordnete Knoten sind für jedes Dateisystem ganz normal. Das heißt, wir nehmen die Dateikennung in hexadezimaler Darstellung und teilen sie in Teile von zwei Zeichen auf. Wir verwenden diese Chunks als Ordnernamen, letzterer als Namen der physischen Datei. GEWINN!
0f/65/84/10/67/68/19/ff.file
Elegant und einfach. Die Dateierweiterung ist hier nicht wichtig. Auf jeden Fall wird die Datei von einem Skript bereitgestellt, das dem Browser einen bestimmten MIME-Typ gibt, den wir auch in der Datenbank speichern. Durch Speichern von Informationen über die Datei in der Datenbank können Sie außerdem den Pfad für den Browser neu definieren. Angenommen, die Datei, die wir haben, befindet sich tatsächlich relativ zum Projektverzeichnis entlang des Pfads
/content/files/0f/65/84/10/67/68/19/ff.file
. In die Datenbank können Sie eine URL schreiben, z. B.
/content/users/678/files/somefile
. SEOs sind im Moment wahrscheinlich ziemlich gelächelt. All dies ermöglicht es uns, uns keine Gedanken mehr darüber zu machen, wo die Datei physisch abgelegt werden soll.
Tabelle in der Datenbank
Zusätzlich zu der Kennung, dem MIME-Typ, der URL und dem physischen Speicherort speichern wir md5- und sha1-Dateien in der Tabelle, um bei Bedarf dieselben Dateien herauszufiltern. Natürlich müssen wir auch Entitätsbeziehungen in dieser Tabelle speichern. Angenommen, die Benutzer-ID, zu der die Dateien gehören. Und wenn das Projekt nicht sehr groß ist, können wir im selben System beispielsweise Fotos von Waren speichern. Daher speichern wir auch den Namen der Entitätsklasse, zu der der Datensatz gehört.
Apropos Vögel. Wenn Sie den Ordner mit .htaccess für den externen Zugriff schließen, kann die Datei nur über das Skript abgerufen werden. Und im Skript wird es möglich sein, den Zugriff auf die Datei zu bestimmen. Mit Blick auf die Zukunft werde ich sagen, dass in meinem CMS (in dem das oben genannte Projekt gerade abgesägt wird) der Zugriff durch grundlegende Benutzergruppen bestimmt wird, von denen ich 8 habe - Gäste, Benutzer, Manager, Administratoren, inaktive, blockierte, gelöschte und Superadministratoren. Super-Admin kann absolut alles, so dass es nicht an der Bestimmung des Zugriffs beteiligt ist. Wenn der Benutzer ein Superadministrator-Flag hat, ist er ein Superadministrator. Alles ist einfach. Das heißt, wir werden den Zugriff auf die verbleibenden sieben Gruppen bestimmen. Der Zugriff ist einfach - entweder geben Sie die Datei oder nicht geben. Insgesamt können Sie ein Feld vom Typ
TINYINT nehmen .
Und noch etwas. Gemäß unserem Gesetz müssen wir benutzerdefinierte Bilder physisch speichern. Das heißt, wir müssen die Bilder irgendwie als gelöscht markieren, anstatt sie physisch zu löschen. Es ist am bequemsten, für diese Zwecke ein Bitfeld zu verwenden. In solchen Fällen verwende ich normalerweise ein Feld vom Typ
INT . Sozusagen reservieren. Darüber hinaus habe ich bereits eine Tradition darin, die
DELETED- Flagge im 5. Bit vom Ende an zu platzieren. Aber es spielt wieder keine Rolle.
Was haben wir als Ergebnis:
create table `files` ( `id` bigint not null auto_increment,
Dispatcher-Klasse
Jetzt müssen wir eine Klasse erstellen, mit der wir Dateien hochladen. Die Klasse sollte die Möglichkeit bieten, Dateien zu erstellen, Dateien zu ersetzen / zu ändern und Dateien zu löschen. Darüber hinaus sollten zwei Punkte berücksichtigt werden. Erstens kann das Projekt von Server zu Server übertragen werden. In der Klasse müssen Sie also eine Eigenschaft definieren, die das Stammverzeichnis der Dateien enthält. Zweitens ist es sehr unangenehm, wenn jemand eine Tabelle in der Datenbank knallt. Sie müssen also die Möglichkeit der Datenwiederherstellung bereitstellen. Mit dem ersten ist im Allgemeinen alles klar. Für die Datensicherung behalten wir uns nur das vor, was nicht wiederhergestellt werden kann.
ID - vom physischen Speicherort der Datei wiederhergestellt
entity_type - nicht wiederhergestellt
Entität - nicht wiederhergestellt
mime - restauriert mit finfo extension
md5 - wird aus der Datei selbst wiederhergestellt
sha1 - aus der Datei selbst wiederhergestellt
Datei - vom physischen Speicherort der Datei wiederhergestellt
URL - nicht wiederhergestellt
Meta - nicht wiederhergestellt
Größe - aus der Datei selbst wiederhergestellt
Erstellt - Sie können Informationen aus einer Datei entnehmen
aktualisiert - Sie können Informationen aus einer Datei entnehmen
Zugang - nicht wiederhergestellt
Flags - nicht wiederhergestellt
Sie können Metainformationen sofort verwerfen. Es ist nicht kritisch für die Funktionsweise des Systems. Für eine schnellere Wiederherstellung müssen Sie den MIME-Typ noch speichern. Gesamt: Entitätstyp, Entitäts-ID, MIME, URL, Zugriff und Flags. Um die Zuverlässigkeit des Systems zu erhöhen, speichern wir Sicherungsinformationen für jeden Zielordner separat im Ordner selbst.
Klassencode <?php class BigFiles { const FLAG_DELETED = 0x08000000;
Betrachten Sie einige Punkte:
-
realRoot - Der vollständige Pfad zum Ordner, wobei das Dateisystem mit einem Schrägstrich endet.
-
webRoot - Der Pfad vom Stammverzeichnis der Site ohne führenden Schrägstrich (siehe unten, warum).
- Als DBMS verwende ich die
MySQLi- Erweiterung.
- Tatsächlich werden die Informationen aus dem Array
$ _FILES als erstes Argument an die
Upload- Methode übergeben.
- Wenn Sie die
Aktualisierungsmethode aufrufen, um die ID einer vorhandenen Datei zu übergeben, wird diese ersetzt, wenn das Eingabearray in
tmp_name nicht leer ist.
- Sie können die Flags von Dateien gleichzeitig löschen und ändern. Anstatt die Dateikennung zu übergeben, müssen Sie dazu entweder ein Array mit Bezeichnern oder eine durch Kommas getrennte Zeichenfolge übergeben.
Routing
Tatsächlich handelt es sich um einige Zeilen in htaccess im Stammverzeichnis der Site (es wird davon ausgegangen, dass mod_rewrite aktiviert ist):
RewriteCond %{REQUEST_URI} ^/content/(.*)$ RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.+)$ content/index.php?file=$1 [L,QSA]
"Inhalt" ist in meinem Fall der Ordner im Stammverzeichnis der Site. Natürlich können Sie den Ordner auch anders benennen. Und natürlich index.php selbst, in meinem Fall im Inhaltsordner gespeichert:
<?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';
Nun, wir selbst schließen das Dateisystem selbst vor externem Zugriff. Legen Sie die
.htaccess
Datei mit nur einer Zeile im Stammverzeichnis des Ordners
content/files
ab:
Deny from all
Zusammenfassung
Mit dieser Lösung können Sie Leistungseinbußen beim Dateisystem aufgrund einer Erhöhung der Anzahl der Dateien vermeiden. Zumindest die Probleme in Form von Tausenden von Dateien in einem Ordner können definitiv vermieden werden. Gleichzeitig können wir den Zugriff auf Dateien an lesbaren Adressen organisieren und steuern. Plus Einhaltung unserer düsteren Gesetzgebung. Machen Sie sofort eine Reservierung, diese Lösung ist KEINE vollständige Möglichkeit, Inhalte zu schützen. Denken Sie daran: Wenn etwas im Browser abgespielt wird, kann es kostenlos heruntergeladen werden.