2014-06-21 87 views
5

我有一個腳本上傳圖像,並根據方向旋轉它們,我遇到的問題是,當上傳具有EXIF標記的圖像時,出現錯誤:PHP允許內存大小用盡圖像上傳輪轉

用盡33554432個字節

允許的內存大小(試圖分配 10368字節。

然後它指的是在錯誤日誌行。

我沒有通知這隻有在具有EXIF標籤的圖像時纔會發生。如果正常的圖像,由Photoshop或上傳的東西上傳,它沒有任何問題。

實際的形象定位代碼如下:

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  
} 

在內存問題發生的error_log中確切的行實際上是一個在那裏說:

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

我的方式在腳本中調用它如下:

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

我基本上是什麼試圖實現的是旋轉後的圖像被保存在原始文件的相同位置,基本上取而代之。

同樣,這隻會發生在包含EXIF信息的圖像上。所有其他上傳沒有問題。

什麼可能導致此內存分配問題?

回答

4

你的錯誤是這樣的:

允許的耗盡33554432個字節內存的大小(試圖分配 10368字節)。

33554432字節轉換爲32兆字節。所以這都意味着PHP在嘗試做一些工作時耗盡內存。

您聲稱失敗的圖像具有EXIF信息,但這並不符合我的原因。 Irregardless,快速解決您的問題是通過添加和ini_set線連接到memory_limit到你的函數

例如增加對你的函數PHP的內存,在此添加你做的if (function_exists('exif_read_data')) {檢查後。我將它設置爲64M,因爲它在運行此功能時將有效地將腳本內存容量加倍。代碼在這裏:

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  
} 

什麼我基本上想實現的是旋轉的圖像得到 保存在同一個地方作爲原始文件,基本上取代它。

的問題是您使用的是PHP GD庫PHP加載文件進入系統時會吃內存,吃起來更內存時它正在嘗試旋轉圖像。

這可能是因爲具有EXIF信息的圖像實際上具有比標準72dpi更高的DPI。所以雖然它們的尺寸可能看起來與沒有EXIF信息的其他圖像相同,但300dpi圖像的尺寸實際上比72dpi圖像大4倍。這很可能是爲什麼這些圖像失敗;不是EXIF信息,而是整體DPI。

現在,您還可以通過更改讀取爲memory_limit = 32M的行來更改php.ini中的內存限制。從技術上講,這是可行的。但我認爲這是一個失敗的函數腳本的好習慣。

這是因爲當您更改php.ini中的設置時,它會增加所有PHP交互的RAM;不只是問題的問題。所以你的服務器突然吃掉了更多的RAM(用於運行PHP的Apache)用於基本功能,以及古怪的功能,吃掉更多的RAM。這意味着如果這些代碼每天只能訪問幾次,那麼爲什麼還要爲每個PHP進程的32M內存感到滿足的大型服務器負載呢?更好地使用ini_set('memory_limit', '64M');來隔離RAM增加需求。

+1

哇,這太奇怪了。因爲實際上我認爲它是一個memory_limit問題,我在php.ini中將其增加到了256MB,但是在看到你的代碼修復了它之後,我再次在php.ini上做了一個grep(我之前沒做過),並注意到有2個不同的memory_limit集。我多麼愚蠢!非常感謝您的幫助,以解決此問題:) – user1227914

+0

@ user1227914太棒了!另外,請查看代碼下方的最新編輯,以解釋爲什麼我認爲你的內存已經被吃掉了,而調整'php.ini'對於這樣的情況可能不是個好主意。 – JakeGould

+0

這是有道理的,我會實施你的建議。非常感謝你真正有用的回覆:) – user1227914

0

imagecreatefromjpeg解壓縮圖像並將結果存入內存。這就是3 MB JPEG需要32 MB內存的原因(具體數量取決於分辨率,顏色深度等)。

你需要這樣的結果,所以你把它分配給一個變量:

$img = imagecreatefromjpeg($fullpath); 

而現在的問題。 imagerotate使用$img資源,將其旋轉並將結果放入內存的新區域,因爲它通過設計返回新的圖像資源,而不是覆蓋給定的$img資源。所以最後,你需要64 MB的內存:

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

證明:

// 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; 

返回:

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) 

正如你所看到的峯值提高到10 MB imagerotate調用後,但像imageflip這樣的函數不會顯示此行爲,因爲它在內部覆蓋了您的變量。

也許你認爲你可以通過覆蓋變量解決:

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

但是,這隻會降低當前使用情況:

After imagerotate: 5 MB (Max: 10 MB) 

這是可悲的,我們can not pass the variable by reference since PHP 5.4

imagerotate(&$image, 180, 0); 

我打開了一個bug report,所以它會有希望優化 未來。

相關問題