Ressourcen wieder freigeben -> Bildfunktionen

Heute mal nur ein ganz kleiner Beitrag, aber man kennt es ja „In der Kürze liegt die Würze“ und „Klein aber oho“. Besonders diese kleinen Tipps können manchmal gute Ergebnisse bringen. Thema ist die Bearbeitung von Bildern mittels der Image-Funktionen von PHP.

Und zwar habe ich ein Script erstellt, das zu große hochgeladene Bilder (man kennt das ja von den Sonstwieviel-MegaPixel-Kameras) so verkleinert, dass sie eine bestimmte Dateigröße nicht überschreiten.
Da es aber leider nicht möglich ist, im Vorhinein die Bildgröße des Bildes (Breite x Höhe) zu bestimmen, bei der das Bild die Dateigröße x hat, musste ich das sequentiell machen. Also erst 90% der Originalgröße, dann 80% usw. bis die Dateigröße kleiner oder gleich der geforderten Grenze war.

Als ich dieses Script testete kam der äußerst unbeliebte Fehler

failed to open stream: Cannot allocate memory

Unbeliebt deshalb, weil man ihn oft nur schwer beheben kann.
Das Problem ist nämlich, dass das Bild als Image-Ressource geladen und anschließend per imagejpeg auf der Platte gespeichert wird. Die Image-Ressource belegt aber auch nach dem Speichern auf der Platte noch Ressourcen (= Arbeitsspeicher). Und wenn man die 90% Größe durch hat, gehts weiter mit 80% (das 90%-Bild ist ja dann eigentlich überflüssig) usw.
Die Ressourcen werden standardmäßig von PHP erst wieder freigegeben, wenn das Script ganz abgearbeitet wurde (rekursive Geschichten sind da also ganz schlecht, wenn man sich nicht um den Speicher kümmert).

Hier also kurz der Ablauf meines Scripts:

  1. Laden der Original-Bilddatei durch ein Formular
  2. Prüfen ob Dateigröße > x (durch das assoziative Array $_FILES[‚xyz‘][’size‘])
  3. Falls 2. true, Funktion zum Verkleinern aufrufen mit 90% Bildgröße
  4. Bild als Image-Ressource laden, Größe per imagesx und imagesy ermitteln und das Zielbild per imagecopyresampled() auf 0,9 der Originalgröße resizen.
  5. per imagejpeg() auf der Festplatte abspeichern
  6. Dateigröße ermitteln, falls immernoch > x,dann zu Schritt 3 mit 10% kleinerer Bildgröße, ansonsten Funktion verlassen

Als Beispiel möchte ich also mal von einem Bild ausgehen, das 1 MB als Image-Ressource belegt (also nicht als jpg, png oder sonstwas sondern PHP-intern als Ressource). Im Worst-Case werden 9 Rekursionsdurchläufe ausgeführt, wenn man immer 10% nach unten geht (90, 80, 70, … , 10).
Das bedeutet, dass sich der belegte Speicher so hochschaukelt (wenn man von einer Proportionalität zwischen belegtem Speicher und Bildgröße ausgeht):
0,9*1000 + 0,8*1000 + 0,7*1000 + … + 0,1*1000 = 900 + 800 + 700 + … + 100 = 4500 kB
Und das obwohl sämtliche bereits abgearbeiteten Bilder gar nicht mehr benötigt werden, weil sie ja als von der Dateigröße zu groß ausgewertet wurden. Es wäre also die 4,5-fache Speichermenge nötig (plus der temporären Datei).

Bei mir geholfen hat letztlich – und damit kommen wir hier mal zum Punkt – die Funktion imagedestroy(). Diese gibt den Speicher für die Bild-Ressource wieder frei. Außerdem kann man per unlink dann noch die gespeicherte Bilddatei entfernen, die ja benötigt wurde, um die Dateigröße zu bestimmen – also man entfernt sie natürlich nur, wenn die Dateigröße dann immernoch über der Dateigrößen-Grenze x ist, ansonsten endet die Rekursion und man nimmt das Bid einfach.
Oben beschriebener Fehler kommt dadurch nicht mehr. Somit ist die Speichernutzung des Apache sehr viel effektiver, also ruhig mal ordentlich und sauber arbeiten ????

Falls jemand Tipps hat, wie man obige Aufgabe schneller erledigen kann, bitte in den Kommentaren posten. Schwachpunkte waren vor allem, dass man nicht ermitteln kann, bei welcher Bildgröße eine Datei eine bestimmte Dateigröße hat sowie das man die resultierende Dateigröße der Image-Ressource erst nach Zwischenspeichern auf der Platte feststellen kann. Vielleicht gibts dafür ja Lösungen, die mir nicht eingefallen sind.


Deprecated: Directive 'allow_url_include' is deprecated in Unknown on line 0