Stell dir vor, du hast ein 5GB grosses Fotoalbum. Das sind 5GB JPEG-Dateien, wo jeweils eine maximal 1MB gross ist. Eher kleiner. Nun sind einige Bilder doppelt. Und "einige" heisst 900MB.
Wie lösche ich die nun? Nehme ich gThumb und klicke "Bearbeiten" und "Search for Duplicates"?
Das hab ich getan. Alle Duplikate werden mir angezeigt. Löschen muss ich sie manuell. Moment mal. Ein Blick auf die Scrollbar am rechten Rand verrät mir, dass ich noch einige Klicks vor mir habe. Etwas schnelleres muss her. Die Idee für dupdel ist geboren. Nun folgt die Geschichte der Entwicklung.
Als erstes schiesst mir GD durch den Kopf. Als zweites PHP. Denn da sind die gd-Funktionen und Funktionen um Bilder zu bearbeiten ja schon drin.
erste Version von dupdel:
- Ich scanne rekursiv alle Verzeichnisse des Fotoalbums durch und speichere die Pfade in eine Liste
- ich erstelle eine Kopie der Liste
- eine Funktion compare() vergleicht nun beide Listen miteinander indem Sie jeweils drei Pixel der Bilder an drei verschiedenen Positionen auf gleiche Farben überprüft.
Duplikate werden auch gefunden und gelöscht. Aber irgendwie dauert es ziemlich lang. O.o
Das zweite "Moment mal" schiesst mir durch den Kopf. PHP hat doch MD5-Funktionen die einen Hash anhand des Inhalts einer Datei generieren. Heisst bei genau denselben Bildern gibt es ja auch genau denselben Hash.
In der zweiten Version von dupdel vergleicht compare() nun die Bilder, indem es wieder die Liste mit den Dateipfaden kopiert, und dann das Ergebnis von md5_file() bei jedem Schleifendurchlauf vergleicht. Auf einen ca. 10MB grossen Testordner funktioniert das auch recht gut. Auf den 5GB-Ordner angewendet, habe ich nach 5 Stunden warten auf das nächste Duplikat dann doch wieder abgebrochen.
Ein Freund hilft mir nun, das ganze noch zu optimieren. MD5-Hashes und Timestamps werden zwischengespeichert. MD5-Hashes, Dateipfade und Timestamps werden in globale Arrays gespeichert. Die - anfangs noch mit einem "DoubleBubblesort" - synchron sortiert werden.
MD5-Hashes, Dateipfade und Timestamps werden in verschiedene Kombinationen von assoziativen Arrays gepackt. Alles funktioniert ziemlich prima auf kleinere Testfälle, aber auf den großen 5GB-Ordner gesehen bringen sie zwar die erwartete höhere RAM-Belegung, aber nicht die erwarteten Geschwindigkeitsvorteile.
Gestern hatten wir dann die entscheidende Idee die uns auf die doch ziemlich einfache Lösung gebracht hat:
- es werden nur die Dateipfade mit den MD5-Hashes als Schlüssel in eine globale assoziative Liste gespeichert.
- ein Vergleich findet schon beim rekursiven scan() der Dateipfade statt.
- wenn array_key_exists() werden die filectime()'s verglichen und das neuere der beiden Bilder gelöscht.
Diese Änderungen brachten zum Effekt, dass fast alle Duplikate, also ungefähr 900 Megabyte in ca. 15 Minuten gelöscht waren. gThumb verrät mir, dass 49 Bilder nicht gelöscht wurden, keiner weiss warum, aber das war mir dann auch egal, die paar Klicks hab ich dann manuell gemacht...
Wer interessiert an dem Quelltext ist oder ähnlich große Fotoalben hat, meldet sich bei mir. ;)
Blogged with Flock