2014-06-21 9 views
5

Ho uno script che carica le immagini e le ruota a seconda dell'orientamento e sto riscontrando il problema che quando un'immagine caricata con tag EXIF ​​viene caricata, viene visualizzato un errore che dice:php consentito dimensione della memoria esaurita sulla rotazione del caricamento dell'immagine

ammessi dimensione della memoria di 33554432 byte esaurito (cercato di allocare 10368 byte.

E poi la linea a cui fa riferimento nel log degli errori.

ho notato che sta accadendo solo con le immagini che hanno tag EXIF. Se vengono caricate immagini normali generate da Photoshop o qualcosa, funziona senza problemi.

Il codice effettivo orientamento dell'immagine è il seguente:

function correctImageOrientation($fullpath) { 
    if (function_exists('exif_read_data')) { 
    $exif = exif_read_data($fullpath); 
    if($exif && isset($exif['Orientation'])) { 
     $orientation = $exif['Orientation']; 
     if($orientation != 1){ 
     $img = imagecreatefromjpeg($fullpath); 
     $deg = 0; 
     switch ($orientation) { 
      case 3: 
      $deg = 180; 
      break; 
      case 6: 
      $deg = 270; 
      break; 
      case 8: 
      $deg = 90; 
      break; 
     } 
     if ($deg) { 
      $img = imagerotate($img, $deg, 0);   
     } 
     // then rewrite the rotated image back to the disk as $filename 
     imagejpeg($img, $fullpath, 100); 
     } // if there is some rotation necessary 
    } // if have the exif orientation info 
    } // if function exists  
} 

La linea è a error_log dove il problema di memoria avviene sia effettivamente quello dove si dice:

$img = imagerotate($img, $deg, 0); 

Il modo in cui sono chiamandolo nello script è il seguente:

$dirname = session::value('user_id'); 
$rotatedfile = '/home/myfolder/public_html/'.$dirname.'/'.$file_name; 
$rotatedfile = $this->correctImageOrientation($rotatedfile); 

Quello che sono fondamentalmente il tentativo di ottenere è che l'immagine ruotata venga salvata nello stesso posto del file originale, sostituendola sostanzialmente.

Ancora, questo succede SOLO con le immagini che contengono informazioni EXIF. Tutti gli altri sono caricati senza problemi.

Cosa potrebbe causare questo problema di allocazione della memoria?

risposta

4

Il tuo errore è questo:

ammessi dimensione della memoria di 33554432 byte esaurito (cercato di allocare 10368 bytes).

33554432 byte convertiti in 32 megabyte. Quindi tutto ciò significa che PHP ha esaurito la memoria durante il tentativo di fare un po 'di lavoro.

Si sostiene che le immagini che non hanno le informazioni EXIF, ma che non mi suona come la causa di questo. Indipendentemente, la rapida soluzione al vostro problema è quello di aumentare la memoria PHP per la vostra funzione aggiungendo e ini_set linea collegata al memory_limit alla funzione

Ad esempio, aggiungere qui dopo aver fatto il check-if (function_exists('exif_read_data')) {. Lo sto impostando su 64M poiché questo raddoppierà la capacità di memoria degli script quando viene eseguita questa funzione. Codice qui:

function correctImageOrientation($fullpath) { 
    if (function_exists('exif_read_data')) { 
    ini_set('memory_limit', '64M'); 
    $exif = exif_read_data($fullpath); 
    if($exif && isset($exif['Orientation'])) { 
     $orientation = $exif['Orientation']; 
     if($orientation != 1){ 
     $img = imagecreatefromjpeg($fullpath); 
     $deg = 0; 
     switch ($orientation) { 
      case 3: 
      $deg = 180; 
      break; 
      case 6: 
      $deg = 270; 
      break; 
      case 8: 
      $deg = 90; 
      break; 
     } 
     if ($deg) { 
      $img = imagerotate($img, $deg, 0);   
     } 
     // then rewrite the rotated image back to the disk as $filename 
     imagejpeg($img, $fullpath, 100); 
     } // if there is some rotation necessary 
    } // if have the exif orientation info 
    } // if function exists  
} 

Quello che sto cercando di realizzare in pratica è che l'immagine ruotata ottiene salvati nello stesso posto del file originale, in pratica la sua sostituzione.

Il problema è che si sta utilizzando la libreria GD in PHP che si mangia la memoria quando il PHP carica il file nel sistema e mangiare ancora di più memoria quando si sta tentando di ruotare l'immagine.

Potrebbe essere che le immagini che hanno informazioni EXIF ​​abbiano effettivamente un DPI superiore rispetto allo standard 72 dpi. Pertanto, anche se le loro dimensioni potrebbero sembrare superficialmente uguali a quelle di un'altra immagine senza informazioni EXIF, un'immagine a 300 dpi sarà effettivamente circa 4 volte più grande di un'immagine a 72 dpi. Il che è molto probabilmente il motivo per cui quelle immagini stanno fallendo; non le informazioni EXIF ​​ma DPI generale.

Ora è anche possibile modificare il limite di memoria in php.ini modificando la linea che si legge come memory_limit = 32M. E tecnicamente questo funzionerebbe. Ma non considero una buona pratica per uno script di funzioni che sta fallendo.

Ecco perché quando si modifica l'impostazione nello php.ini aumenta la RAM per tutte le interazioni PHP; non solo il problema. Quindi il tuo server sta improvvisamente consumando più RAM per Apache (che esegue PHP) per le funzioni di base e la funzione oddball che consuma più RAM. Significa che se si accede a questo codice solo una manciata di volte al giorno, allora perché caricare il server più grande che è più che felice con 32M di RAM per processo PHP? È meglio utilizzare ini_set('memory_limit', '64M'); per isolare le esigenze di aumento della RAM in questo modo.

+1

Wow, è così strano. Perché in realtà ho considerato un problema memory_limit e l'ho aumentato a 256MB in php.ini ma dopo aver visto che il tuo pezzo di codice l'ha risolto, ho fatto di nuovo un grep su php.ini (cosa che non ho fatto prima) e notato che c'erano 2 diversi set di memoria_limite. Che stupido da parte mia! Grazie mille per il tuo aiuto nel risolvere questo :) – user1227914

+0

@ user1227914 Great!Inoltre, controlla la mia ultima modifica sotto il codice che spiega il motivo per cui penso che la tua RAM venga consumata così tanto e mentre aggiustando 'php.ini' potrebbe non essere una buona idea per un caso come questo. – JakeGould

+0

Questo ha senso e implementerò i tuoi suggerimenti. Grazie mille per la risposta davvero utile :) – user1227914

0

imagecreatefromjpeg decomprime l'immagine e inserisce il risultato nella memoria. Questo è il motivo per cui un JPEG da 3 MB richiede talvolta 32 MB di memoria (l'importo esatto dipende dalla risoluzione, dalla profondità del colore, ecc.).

avete bisogno di questo risultato, in modo da assegnare ad una variabile:

$img = imagecreatefromjpeg($fullpath); 

E ora il problema. imagerotate utilizza la risorsa $img, la ruota e inserisce il risultato in una nuova regione della memoria mentre restituisce progettando una nuova risorsa immagine invece di sovrascrivere la risorsa $img specificata. Così alla fine hai bisogno di 64 MB di memoria:

$img = imagerotate($img, $deg, 0); 

Proof:

// our image 
$url = 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Pluto-01_Stern_03_Pluto_Color_TXT.jpg/1024px-Pluto-01_Stern_03_Pluto_Color_TXT.jpg'; 
file_put_contents('../cache4/' . basename($url), fopen($url, 'r')); 
$filename = '../cache4/' . basename($url); 

echo 'Before imagecreate: ' . round(memory_get_usage()/pow(1024, 2)) . ' MB (Max: ' . round(memory_get_peak_usage()/pow(1024, 2)) . ' MB)<br>' . PHP_EOL; 

// read image into RAM for further usage 
$image = imagecreatefromjpeg($filename); 

echo 'After imagecreate: ' . round(memory_get_usage()/pow(1024, 2)) . ' MB (Max: ' . round(memory_get_peak_usage()/pow(1024, 2)) . ' MB)<br>' . PHP_EOL; 

// rotate image 
$result = imagerotate($image, 180, 0); 

echo 'After imagerotate: ' . round(memory_get_usage()/pow(1024, 2)) . ' MB (Max: ' . round(memory_get_peak_usage()/pow(1024, 2)) . ' MB)<br>' . PHP_EOL; 

// flip image 
imageflip($result, IMG_FLIP_VERTICAL); 

echo 'After imageflip: ' . round(memory_get_usage()/pow(1024, 2)) . ' MB (Max: ' . round(memory_get_peak_usage()/pow(1024, 2)) . ' MB)<br>' . PHP_EOL; 

Returns:

Before imagecreate: 0 MB (Max: 0 MB) 
After imagecreate: 5 MB (Max: 5 MB) 
After imagerotate: 10 MB (Max: 10 MB) 
After imageflip: 10 MB (Max: 10 MB) 

Come si può vedere il picco sale a 10 MB dopo imagerotate è stato chiamato, ma una funzione come imageflip non mostra questo comportamento poiché sovrascrive internamente la variabile.

Forse si pensa si può risolvere che sovrascrivendo la variabile:

$image = imagerotate($image, 180, 0); 

Ma questo solo ridurre l'uso corrente:

After imagerotate: 5 MB (Max: 10 MB) 

E 'triste che ci can not pass the variable by reference since PHP 5.4:

imagerotate(&$image, 180, 0); 

Ho aperto un bug report quindi si spera ottimizzato in il futuro.

Problemi correlati