PHP Bilder verkleinern: Out of Memory

Blackbenji

Lieutenant
Registriert
Nov. 2009
Beiträge
565
Hallo,

beim Bildhochladen erhalte ich folgende Meldung:

Fatal error: Out of memory (allocated 59244544) (tried to allocate 16000 bytes) in /{pfad}/class/media.inc.php on line 123

PHP:
    private function load($filename)
    {
        $extension = $this->getExtension($filename);

        if ($extension == 'jpg' || $extension == 'JPG' || $extension == 'jpeg' || $extension == 'JPEG') {
            $this->image = imagecreatefromjpeg($filename);
        } elseif ($extension == 'gif' || $extension == 'GIF') {
            $this->image = imagecreatefromgif($filename);
        } elseif ($extension == 'png' || $extension == 'PNG') {
            $this->image = imagecreatefrompng($filename);
        }
    }

die entsprechende Zeile ist
$this->image = imagecreatefromjpeg($filename);

Infos zum Bild:
PHP:
Pixel 3000 × 4000
Größe 4.400.925 Byte

Meine php.ini im Scriptordner (nicht die Globale) enthält noch folgende Einträge:
PHP:
upload_max_filesize = 20M
post_max_size = 20M
max_execution_time = 300
memory_limit = 200M

Hat jemand vielleicht eine Idee?
Das Internet spuckt zwar Ideen aus, aber nichts hilft bisher :-(
 
Naja du verbrauchst zu viel Speicher... Extern helfen is da nicht.
 
Wenn dein Bild 3000x4000 Pixel groß ist dann sind das eben mal 36MB, da beißt die Maus keinen Faden ab.
Vielleicht imagemagick nutzen für das Bild?
 
Wird das lokal getestet ? Weil dann kannst du ja die php.ini anpassen und einfach mehr Speicher eintragen dann sollten 36MB hochladen auch kein Problem darstellen. Mal abgesehen von Sicherheitsrisiken.

Edit:

Ich seh gerade scheint Online zu sein mit einer Beschränkung von 20MB. Da wirst du wohl nicht drum rum kommen die Bilder voher schon zu verkleinern.
 
soweit ich imagemagick verstanden habe, muss das installiert werden, oder?
da ich nur einen webspace ohne root rechte habe, werde ich damit nicht viel anfangen können ....
 
Vielleicht hast du ja Glück und es schon installiert? Mit
PHP:
<?php
passthru("which convert");
?>
kannst du es finden, falls es installiert ist.


Wenn es nicht installiert ist, kommt keine ausgabe.
Dann könnte man höchstens versuchen, die Binaries herunterzuladen und in deinen Home-Ordner zu entpacken. Aber ob man da alle Abhängigkeiten hinbekommt, keine Ahnung.
 
Zuletzt bearbeitet:
Mumpitzelchen schrieb:
Wenn dein Bild 3000x4000 Pixel groß ist dann sind das eben mal 36MB, da beißt die Maus keinen Faden ab.
36MB? Schön wärs... Du hast dich um ein ganz paar MB verzählt. Bildbearbeitung läuft auf unkomprimierten Daten.

Blackbenji schrieb:
soweit ich imagemagick verstanden habe, muss das installiert werden, oder?
da ich nur einen webspace ohne root rechte habe, werde ich damit nicht viel anfangen können ....

Die PHP-Bibliothek imagick ist doch oftmals bereits enthalten. Zur Not kann man auch mit seinem Hoster reden oder zu einem gehen, der eben mit sich reden lässt.
 
Troublegum schrieb:
Vielleicht hast du ja Glück und es schon installiert? Mit
PHP:
<?php
passthru("which convert");
?>

als output bekomme ich

PHP:
/usr/bin/convert

aber mir bringt imagemagick wenig, wenn ich das endprodukt öffentlich machen will und diese abhängigkeit drin habe.
als einzige brauchbare lösung bleibt es also nur dateigröße und bildgröße zu beschränken.

gibt es dazu werte die als richtlinien dienen können?
 
Nö. Deshalb gibt man für die Resize-Funktion auch eine Obergrenze per User-Einstellung an. Wenn Bild > Limit -> kein Resize oder sonstige Bildbearbeitung.

Denk einfach dran, dass deine User im Zweifel mit ihrem Smartphone rumknipsen -> ~8MPix @ 3Byte/Pixel musst du mindestens im RAM unterbringen, zzgl. der Variablen des PHP-Scripts.
 
@Daaron: Die ~36MB stimmen so schon. Wie du selbst schon geschrieben hast, benötigt ein Pixel unkomprimiert 3Byte. Bei einem 12MPixel Bild wie es Blackbenji hochladen will eben 36MB.
 
ich hätts echt im kopf rechnen sollen... irgendwo auf dem taschenrechner hab ich mich wohl vertippt. stand das ding auf Oct? was hab ich bloß gemacht?
 
Hallo,

ich bräuchte noch einmal einen Tipp von euch.

Das EXIF Thema (anderer Thread) und Rotation hab ich nun Lokal behoben.

PHP:
                    $exist = $this->fileExist($filename);
                    if($exist == 'false') {

                        $imageWithPath = self::original . $filename;

                        $exif = exif_read_data($_FILES['datei']['tmp_name']);
                        $res = imagecreatefromjpeg($_FILES['datei']['tmp_name']);
                        if (!empty($exif['Orientation'])) {
                            switch ($exif['Orientation']) {
                                case 3:
                                    $res = imagerotate($res, 180, 0);
                                    break;
                                case 6:
                                    $res = imagerotate($res, -90, 0);
                                    break;

                                case 8:
                                    $res = imagerotate($res, 90, 0);
                                    break;
                            }
                        }
                        imagejpeg ($res, $_FILES['datei']['tmp_name'], 65);
                        imagedestroy($res);

                        move_uploaded_file($_FILES['datei']['tmp_name'], $imageWithPath);

Lokal funktioniert es wunderbar!
Auf dem 1und1 Webspace aber nicht :(

Fatal error: Out of memory (allocated 59244544) (tried to allocate 9792 bytes) in ...

Es wird in der Zeile $res = imagerotate($res, -90, 0); der OOM ausgelöst.



Im Netz habe ich eine Funktion gefunden, mit der man sich memory usage ausgeben lassen kann:

PHP:
error_reporting(E_ALL);
ini_set('display_errors', 1);
function foo()
{
    $x = memory_get_usage(true);
    printf('%.2f ', $x/1048576);
}

echo PHP_VERSION . " <br/>";
foo();
$e = str_repeat('a',1024);
$e = str_repeat($e, 2048); // 2MB

foo();
$a = str_repeat($e, 4);
for($i=0; $i<80; $i++)
{
    if ($i%8==0) echo "<br />\n";
    $a .= $e;
    foo();
}

auf dem 1und1 server:

PHP:
5.4.12 
0.25 2.50 
12.75 14.75 16.75 18.75 20.75 22.75 24.75 26.75 
28.75 30.75 32.75 34.75 36.75 38.75 40.75 42.75 
44.75 46.75 48.75 50.75 52.75 54.75 56.75 
Fatal error: Out of memory (allocated 59506688) (tried to allocate 58720257 bytes) in memory.php on line 28

Also liege ich irgendwo bei 58 MB Speicher.

Das Bild in Werten:
2.922.802 Byte
2448 × 3264 Pixel
72 Pixel/Zoll
JPEG-Bild


Mache ich Fehler oder muss das Bild noch kleiner?
Muss ich meinem User echt aufzwingen vor dem Upload eines Bildes die Bilddaten zu verkleinern?

Wie machen das andere Scripte zb Wordpress und co ?
 
Ich habe früher mal an einer größeren Foren-Software mitgearbeitet.
Damals haben wir bei riesigen Fotos auch gerne die in den EXIF-Daten eingebetteten Thumbnails verwendet,
wenn die Bilder zu groß waren. Ist natürlich auch nicht immer ideal, denn die EXIF Thumbnails werden ja nicht von jeder Software aktualisiert/erstellt.

Ich bin mittlerweile dort nicht mehr bei der Entwicklung dabei, aber ich habe gerade mal in die aktuelle Implementierung geguckt. Die Bild- und Thumbnail-Funktionen wurden in einem eigener Klasse abstrahiert und es wird wenn möglich die ImageMagick-Klasse verwendet, ansonsten GDI (was du ja auch schon tust).


Du darfst auch nicht vergessen, dass der imagerotate() Funktion ja mit einer Kopie deiner Resource arbeitet. :(
Wordpress hat die gleichen Probleme mit riesigen Bildern wie du.
 
Zuletzt bearbeitet:
Blackbenji schrieb:
Wie machen das andere Scripte zb Wordpress und co ?

Da geht einfach jeder davon aus, dass dem PHP auch genug Speicher erlaubt wird. Du hast da null Chancen, wenn die eine Operation wirklich soviel frisst und du nicht noch Megabyteweise Datenmüll im Ram hast, den du eigentlich entfernen könntest.
 
@ice-breaker: netter ansatz!

ich habe mal meine media-class aus dem projekt genommen und außerhalb laufen lassen.

PHP:
<?php
/**
 *
 * Copyright (C) 2013 Blackbenji
 *
 * @package ---
 * @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL
 */

// path patch for windows, unix and mac
if (DIRECTORY_SEPARATOR == '/') {
    $path = dirname(__FILE__);
} else {
    $path = str_replace('\\', '/', dirname(__FILE__));
}
define('DOCROOT', $path . "/");

define('UPLOADROOT', DOCROOT . 'upload/');
define('UPLOAD_ORIGINAL', UPLOADROOT . "original/" );
define('UPLOAD_THUMBNAIL', UPLOADROOT . "thumbnail/" );

gc_enable();

include('class/media.inc.php');

$media = new Media();

var_dump($media->upload());


Und dann:
PHP:
gc_collect_cycles();
$res = imagerotate($res, -90, 0);

bringt dann.
Fatal error: Out of memory (allocated 59244544) (tried to allocate 2448 bytes) in /class/media.inc.php on line 62

welche Ram Optimierungen könnte man noch versuchen?
 
Zuletzt bearbeitet:
Blackbenji schrieb:
Mache ich Fehler oder muss das Bild noch kleiner?
Muss ich meinem User echt aufzwingen vor dem Upload eines Bildes die Bilddaten zu verkleinern?

Wie machen das andere Scripte zb Wordpress und co ?
Gar nicht. Wordpress legt hardcoded den Speicherbedarf standardmäßig 256MB als Maximalwert. Andere Systeme nehmen sich nicht standardmäßig so dreist viel, unter 64MB arbeitet da aber trotzdem keins.
Bei Contao gibt man z.B. als Admin eine maximale Auflösung an, bis zu der Größenveränderungen etc. durchgeführt werden. Hier tastet man sich mit Versuch+Irrtum an das Limit heran, oder man arbeitet einfach mit recht konservativen Werten. Tatsächlich hatte ich aber bei 64MB Memory Limit nie Probleme bei ca. 6MPix.

Und ja, du musst deine User wohl dazu erziehen, endlich nicht hirnlos mit drölfzillionen MPix großen Aufnahmen ihrer Katze herumzuspielen. Die sollen gefälligst zuhause bereits die Bilder vorbereiten... Exif entsorgen etc.
 
@Daaron: Harcode im Sinne von:
PHP:
@ini_set( 'memory_limit', '256M' );
?

Ich versteh es gerade nicht so ganz. Auf dem 1und1 Webspace habe ich gerade das aktuellste Wordpress installiert und das gleiche Bild was bei mir nicht funktioniert, dort hochgeladen.
Bis auf den Rotationsbug (EXIF) wurde das Bild hochgeladen inkl. Thumbnail.

Ich habe bei mir ebenfalls den Eintrag auf:
PHP:
@ini_set( 'memory_limit', '256M' );
gesetzt, jedoch ohne Erfolg.

Frustriert einen ganz schön :(
 
Höchstwahrscheinlich ist Klasse, die bei WP für die Bildbearbeitung zuständig ist, effektiver als deine. Genau deshalb heißt die Grundregel auch: Schreibe nichts neu, was jemand anderes bereits quelloffen gelöst hat.

Was Wordpress angeht:
PHP:
if ( current_user_can( 'manage_options' ) )
	@ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
Wobei "WP_MAX_MEMORY_LIMIT" in einer anderen Datei auf 256MB gesetzt wird. Ich glaube, der Wert lässt sich in den Optionen überschreiben, aber der Standard sind diese 256MB... was übrigens ein drastisch erhöhtes Log-Aufkommen im syslog auslöst, wenn PHP Suhosin läuft und eben wie üblich nach syslog loggt.

Wenn du bei was wirklich Gutem abschreiben willst:
https://github.com/contao/core/blob/hotfix/2.11.12/system/libraries/Controller.php
Ab Zeile 892 bzw. 908
 
@Daaron: danke.

Habe jetzt ein wenig die Foren durchsucht und das Ergebnis: 1und1 hat Memory Jobs am laufen, die sobald einer über die Grenze geht, den Prozess beenden. Ich kann also memory_limit eintragen wie ich will, es wird nie dazu kommen, auch wenn PHP INFO mir meine geänderten Daten anzeigt.

Über einen Testaccount bei all-inkl. habe ich das jetzt ausprobiert und kann hier zb 512 mb eintragen. laut dem memory test (weiter oben) werden auch 512 mb benutzt.

auf 120 mb lief mein script dann vernünftig durch.
werde langfristig wohl wechseln ...
 
Zurück
Oben