2014-06-21 14 views
5

Tôi có một tập lệnh tải lên hình ảnh và xoay chúng tùy theo hướng và tôi gặp sự cố khi một hình ảnh có thẻ EXIF ​​được tải lên, tôi nhận được thông báo lỗi:Kích thước bộ nhớ php cho phép đã cạn kiệt trên xoay hình ảnh tải lên

phép kích thước bộ nhớ của 33.554.432 byte kiệt sức (cố gắng phân bổ 10.368 byte.

Và sau đó dòng nó được đề cập đến trong bản ghi lỗi.

tôi đã thông báo rằng nó chỉ xảy ra với những hình ảnh có thẻ EXIF. Nếu hình ảnh bình thường, được tạo ra bởi Photoshop hoặc một cái gì đó được tải lên, nó hoạt động mà không có vấn đề.

Mã định hướng hình ảnh thực tế như sau:

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  
} 

Dòng chính xác trong error_log nơi mà vấn đề bộ nhớ xảy ra thực sự là một trong những nơi nó nói:

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

Cách tôi gọi nó trong kịch bản là như sau:

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

Điều tôi cơ bản cố gắng đạt được là hình ảnh xoay được lưu ở cùng một vị trí với tệp gốc, về cơ bản sẽ thay thế nó.

Một lần nữa, điều này chỉ xảy ra với hình ảnh có chứa thông tin EXIF. Tất cả những người khác được tải lên mà không gặp sự cố.

Điều gì có thể gây ra sự cố phân bổ bộ nhớ này?

Trả lời

4

lỗi của bạn là thế này:

phép kích thước bộ nhớ của 33.554.432 byte kiệt sức (cố gắng phân bổ 10.368 byte).

33554432 byte chuyển thành 32 megabyte. Vì vậy, tất cả điều này có nghĩa là PHP hết bộ nhớ trong khi cố gắng thực hiện một số công việc.

Bạn cho rằng hình ảnh không có thông tin EXIF, nhưng điều đó không đúng với tôi là nguyên nhân của việc này. Không quan tâm, giải pháp nhanh cho vấn đề của bạn là tăng bộ nhớ PHP cho chức năng của bạn bằng cách thêm và ini_set đường kết nối với memory_limit vào chức năng của bạn

Ví dụ: thêm nó tại đây sau khi bạn thực hiện kiểm tra if (function_exists('exif_read_data')) {. Tôi đang đặt nó thành 64M vì điều đó sẽ có hiệu quả gấp đôi dung lượng bộ nhớ tập lệnh của bạn khi nó chạy chức năng này. Mã đây:

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  
} 

Những gì tôi đang cố gắng để đạt cơ bản là hình ảnh xoay được lưu trong cùng một vị trí như các tập tin ban đầu, về cơ bản thay thế nó.

Vấn đề là bạn đang sử dụng thư viện GD bằng PHP sẽ ăn bộ nhớ khi PHP tải tệp vào hệ thống và ăn nhiều bộ nhớ hơn khi cố gắng xoay hình ảnh.

Có thể các hình ảnh có thông tin EXIF ​​thực sự có DPI cao hơn 72dpi chuẩn. Vì vậy, mặc dù kích thước của chúng có vẻ bề ngoài giống như một hình ảnh khác mà không có thông tin EXIF, hình ảnh 300dpi sẽ có kích thước lớn gấp 4 lần so với hình ảnh 72dpi. Đó là khả năng nhất tại sao những hình ảnh đó không thành công; không phải là thông tin EXIF ​​mà là toàn bộ DPI.

Bây giờ bạn cũng có thể thay đổi giới hạn bộ nhớ trong php.ini bằng cách thay đổi dòng có thể đọc là memory_limit = 32M. Và về mặt kỹ thuật, điều này sẽ hiệu quả. Nhưng tôi không coi đó là cách thực hành tốt cho một tập lệnh chức năng không thành công.

Đó là vì khi bạn thay đổi cài đặt trong php.ini, nó sẽ tăng RAM cho tất cả các tương tác PHP; không chỉ vấn đề vấn đề. Vì vậy, máy chủ của bạn đột nhiên ăn nhiều RAM hơn cho Apache (chạy PHP) cho các chức năng cơ bản cũng như chức năng kỳ quặc ăn nhiều RAM hơn. Có nghĩa là nếu mã này chỉ được truy cập một vài lần một ngày, thì tại sao gánh nặng máy chủ lớn hơn là hạnh phúc hơn với 32M RAM cho mỗi quá trình PHP? Sử dụng tốt hơn ini_set('memory_limit', '64M'); để cô lập nhu cầu tăng RAM như thế này.

+1

Chà, đó là SO lạ. Bởi vì tôi thực sự đã có nó được coi là một vấn đề memory_limit và tôi tăng nó lên 256MB trong php.ini nhưng sau khi thấy rằng đoạn mã của bạn đã sửa nó, tôi đã làm một grep trên php.ini một lần nữa (mà tôi đã không làm trước đó) và nhận thấy có 2 bộ nhớ memory_limit khác nhau. Cách ngu ngốc của tôi! Cảm ơn bạn rất nhiều vì đã giúp đỡ bạn trong việc sửa lỗi này :) – user1227914

+0

@ user1227914 Tuyệt vời!Ngoài ra, hãy kiểm tra chỉnh sửa mới nhất của tôi bên dưới mã giải thích lý do tại sao tôi nghĩ RAM của bạn đang bị ăn quá nhiều và trong khi điều chỉnh 'php.ini' có thể không phải là một ý tưởng tuyệt vời cho một trường hợp như thế này. – JakeGould

+0

Điều đó có ý nghĩa và tôi sẽ thực hiện các đề xuất của bạn. Cảm ơn bạn rất nhiều vì đã trả lời rất hữu ích :) – user1227914

0

imagecreatefromjpeg giải nén hình ảnh và đặt kết quả vào bộ nhớ. Đó là lý do tại sao một 3 MB JPEG cần đôi khi 32 MB bộ nhớ (Số tiền chính xác phụ thuộc vào độ phân giải, độ sâu màu, vv).

Bạn cần kết quả này, vì vậy bạn gán nó vào một biến:

$img = imagecreatefromjpeg($fullpath); 

Và bây giờ là vấn đề. imagerotate sử dụng tài nguyên $img, xoay vòng và đặt kết quả vào vùng mới của bộ nhớ khi nó trả về bằng cách thiết kế một tài nguyên hình ảnh mới thay vì ghi đè tài nguyên $img đã cho. Vì vậy, cuối cùng bạn cần 64 MB bộ nhớ:

$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) 

Như bạn có thể nhìn thấy đỉnh tăng đến 10 MB sau imagerotate đã được gọi là, nhưng một hàm như imageflip không hiển thị hành vi này vì nó sẽ ghi đè lên biến nội bộ của bạn.

Có lẽ bạn nghĩ rằng bạn có thể giải quyết rằng bằng cách ghi đè biến:

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

Nhưng điều đó sẽ chỉ làm giảm việc sử dụng hiện tại:

After imagerotate: 5 MB (Max: 10 MB) 

Thật buồn là chúng ta can not pass the variable by reference since PHP 5.4:

imagerotate(&$image, 180, 0); 

Tôi đã mở một bug report để nó được hy vọng sẽ được tối ưu hóa tương lai.

Các vấn đề liên quan