Karem
Karem

Reputation: 18103

PHP: Handle memory & code low memory usage

Yes I am having an issue with PHP memory. My host offer max. to use 64M memory limit, and somehow when i upload+crop functions exceeds this memory, and im receiving an error(not always though, depends on image size)

When I upload an image with around 1 mb or lower, it uploads, and you can crop and save it.

But when i upload an image with around 2 mb or higher (my limit on the uploadfunction is 6mb), it uploads, and then when you crop the image and want to save it i get "out of memory error."

I tried to imagedestroy() after any function in gd, i don't use anymore, but this didn't affect it. Although im only using GDfunctions in the case crop

So is there anything that i can free my memory off in the case upload?

Or maybe in general how to free the php memory usage?

Here's my script:

switch($action) {

    case 'upload':


    $userid = $USER;
    $user = getUserInfo($userid);
    $lasttime = $user['showURP']["photo_lasttime"];

    $upload_name = "file";
    $max_file_size_in_bytes = 8388608;
    $extension_whitelist = array("jpg", "gif", "png", "jpeg");

    /* checking extensions */
    $path_info = pathinfo($_FILES[$upload_name]['name']);
    $file_extension = $path_info["extension"];
    $is_valid_extension = false;
    foreach ($extension_whitelist as $extension) {
        if (strcasecmp($file_extension, $extension) == 0) {
            $is_valid_extension = true;
            break;
        }
    }
    if (!$is_valid_extension) {
        echo "{";
        echo        "error: 'Filtype not allowed!'\n";
        echo "}";
        exit(0);
    }
    /* checking filetype*/
switch(exif_imagetype($_FILES[$upload_name]['tmp_name'])) {
    case IMAGETYPE_GIF:
    case IMAGETYPE_JPEG:
    case IMAGETYPE_PNG:
        break;
    default:
        echo "{";
        echo        "error: 'This is no real image!'\n";
        echo "}";
        exit(0);
}

    /* file size check */
    $file_size = @filesize($_FILES[$upload_name]["tmp_name"]);
    if (!$file_size || $file_size > $max_file_size_in_bytes) {
        echo "{";
        echo        "error: 'Filesize exceeds the limit: 6MB'\n";
        echo "}";
        exit(0);
    }




    if(isset($_FILES[$upload_name]))
        if ($_FILES[$upload_name]["error"] > 0) {
            echo "Error: " . $_FILES["file"]["error"] . "<br />";
            } else  {
                $userfile = stripslashes($_FILES[$upload_name]['name']);
                $file_size = $_FILES[$upload_name]['size'];
                $file_temp = $_FILES[$upload_name]['tmp_name'];
                $file_type = $_FILES[$upload_name]["type"];
                $file_err = $_FILES[$upload_name]['error'];
                $file_name = $userfile;

                if(move_uploaded_file($file_temp, "images/profilePhoto/".$file_name)) {
                    echo "{";
                    echo        "msg: '".$file_name."'";
                    echo "}";
                }
            }
    break;

    case 'crop':
    if ($_SERVER['REQUEST_METHOD'] == 'POST')   {


        $jpeg_quality = 90;

        $src = "images/profilePhoto/".$_POST['fname'];
        $ext= pathinfo($src, PATHINFO_EXTENSION);


    if($ext == "jpg" OR $ext == "jpeg" OR $ext == "JPG"){
        $img_r = imagecreatefromjpeg($src); // <-- im getting out of memory error on this line

    }elseif($ext == "png" OR $ext == "PNG"){
        $img_r = imagecreatefrompng($src);
    }elseif($ext == "gif" OR $ext == "GIF"){
        $img_r = imagecreatefromgif($src);
    }

        if($_POST['fixed'] == 0) {
           $targ_w = $_POST['w'];
           $targ_h = $_POST['h'];
        }
        else {
            $targ_h = $_POST['sizeh']; 
            $targ_w = $_POST['sizew']; 
        }

        $dst_r = ImageCreateTrueColor( $targ_w, $targ_h );

        imagecopyresampled($dst_r,$img_r,0,0,$_POST['x'],$_POST['y'],
        $targ_w,$targ_h,$_POST['w'],$_POST['h']);
        imagedestroy($img_r); // free memory

    $path_thumbs = "images/profilePhoto/thumbs";
        $filename = $USER . ".jpg";
        $thumb_path = $path_thumbs . '/' . $filename;

        imagejpeg($dst_r,$thumb_path,$jpeg_quality);
        imagedestroy($dst_r); // free memory

        echo json_encode(array(
            'filename'=>$thumb_path
        ));
        unlink($src);       


    }   
    break;
}

My host is only supporting GD lib for cropping images, so imagemagick cant be used.

And as i mentioned previously, my host has a memory_limit on 64M, that cant be set

Upvotes: 2

Views: 2311

Answers (4)

Prescol
Prescol

Reputation: 627

You should resize the image on client browser instead on server. This is made with html5 canvas element. You can write your own function or use a plugin. There is a good jquery plugin to do this.

Blueimp jQuery File Upload

Upvotes: 0

Klinky
Klinky

Reputation: 2092

Unfortunately you are going to be constrained by your memory limit. The GD library looks like it has horrid memory management. I just tried loading an image with the dimensions of 3951x2520 & my scripts memory usage went from 512KB before load to about 50MB after load(PHP 5.3.3, Windows 7 Home x64). The actual image should only be taking up about 28.49MB of memory, so why is GD taking almost double this amount? I have no idea.

Edit: Actually it looks like the reason is because GD stores the image in it's internal GD2 format which is not memory efficient.

Edit2: It appears GD2 format actually is only bloated by about 67% over an actual truecolor bitmap. Surprisingly the original GD format takes a smaller footprint than the original image itself or GD2. This I believe is due to dithering, which is noticeable on the output. The only way I can see to get a file loaded into original GD format is to first load it using it's image function(e.g. imagecreatefromjpeg) and then save it using imagegd (not imagegd2) then reload it back in with imagecreatefromgd. This is not very efficient and dithering definitely occurs.

Final Edit:

This I feel is a better function than the one I had earlier below. You give it the filename of your file along with the target height & width & it will check available memory and return true or false if you have enough to perform the resize. If you would prefer to just know approx how much memory is required then you can pass true as the $returnRequiredMem parameter.

function checkMemAvailbleForResize($filename, $targetX, $targetY,$returnRequiredMem = false, $gdBloat = 1.68) {
    $maxMem = ((int) ini_get('memory_limit') * 1024) * 1024;
    $imageSizeInfo = getimagesize($filename);
    $srcGDBytes = ceil((($imageSizeInfo[0] * $imageSizeInfo[1]) * 3) * $gdBloat);
    $targetGDBytes = ceil((($targetX * $targetY) * 3) * $gdBloat);
    $totalMemRequired = $srcGDBytes + $targetGDBytes + memory_get_usage();
    if ($returnRequiredMem) return $srcGDBytes + $targetGDBytes;
    if ($totalMemRequired > $maxMem) return false;
    return true;
}

Examples:

if (!checkMemAvailableForResize('path/to/file.jpg', 640,480)) die('Cannot resize!');

or:

$totalBytesNeeded = checkMemAvailableForResize('path/to/file.jpg',640,480,true);

Additionally you can adjust the $gdBloat parameter in case 68% is too little or too much overhead:

checkMemAvailableForResize('path/to/file.jpg',640,480,false, 1.69);

Upvotes: 4

homelessDevOps
homelessDevOps

Reputation: 20726

Filesize doesnt matter on jpeg, because the stuff is compressed. If you work with this image, it is stored as bitmap in memory, and i think thats your Problem.

You could try ImageMagick because it does not need as much memory as gd!

ImageMagick on php.net

Upvotes: 0

Amber
Amber

Reputation: 526603

Keep in mind that while GD is manipulating an image, it's not stored in a compressed format. Thus, the amount of memory GD will need to work with the image is directly proportional to its size (width x height), whereas the image you're uploading is probably compressed, and thus may be much smaller on your own disk.

For instance, you might be uploading a 1MB file, but that file is actually 1024x1024 pixels and thus would take something like ~4MB of memory for GD to load - or if it were even larger (say 2048x2048) it would be more like 16MB of memory, etc.

Upvotes: 1

Related Questions