PHP - Compress Image to Meet File Size Limit

前端 未结 3 1810
日久生厌
日久生厌 2021-01-16 13:30

I have to upload image files that meet a max width dimension and max file size.

I have the code that checks width size and resizes the image to meet the max image w

相关标签:
3条回答
  • 2021-01-16 14:15

    Unfortunately, estimating final file size isn't something you can do as you have no access to the JPEG encoder's internal state.

    One thing you can do is compress a smaller version of the image and extrapolate from there. Reducing the width and height by four means the computer has to deal with only one sixteenth of the pixels. The trick here is how to prepare a representative test image. Scaling the image down using imagecopyresampled() or imagecopyresized() won't work. It changes the compression characteristic too much. Instead, what you want to do is copy every fourth 8x8 tile from the original image:

    $width1 = imagesx($img1);
    $height1 = imagesy($img1);
    $width2 = floor($width1 / 32) * 8;
    $height2 = floor($height1 / 32) * 8;
    $img2 = imagecreatetruecolor($width2, $height2);
    for($x1 = 0, $x2 = 0; $x2 + 8 < $width2; $x1 += 32, $x2 += 8) {
        for($y1 = 0, $y2 = 0; $y2 + 8 < $height2; $y1 += 32, $y2 += 8) {
            imagecopy($img2, $img1, $x2, $y2, $x1, $y1, 8, 8);
        }
    }
    

    Compress the smaller image at various quality levels and get the respected file sizes. Multiplying them by 16 to should yield a reasonably good estimate of what you would get with the original image.

    0 讨论(0)
  • 2021-01-16 14:25

    No. It's the optimum possible.

    0 讨论(0)
  • 2021-01-16 14:33

    Here is a way to achieve that. It does not save a file until it is satisfied that the file size will be lower than the specified maximum.

    $original_file = 'original.jpg';
    $resized_file = 'resized.jpg';
    $max_file_size = '30000'; // maximum file size, in bytes
    
    $original_image = imagecreatefromjpeg($original_file);
    
    $image_quality = 100;
    
    do {
        $temp_stream = fopen('php://temp', 'w+');
        $saved = imagejpeg($original_image, $temp_stream, $image_quality--);
        rewind($temp_stream);
        $fstat = fstat($temp_stream);
        fclose($temp_stream);
    
        $file_size = $fstat['size'];
    }
    while (($file_size > $max_file_size) && ($image_quality >= 0));
    
    if (-1 == $image_quality) {
        echo "Unable to get the file that small. Best I could do was $file_size bytes at image quality 0.\n";
    }
    else {
        echo "Successfully resized $original_file to $file_size bytes using image quality $image_quality. Resized file saved as $resized_file.\n";
        imagejpeg($new_image, $resized_file, $image_quality + 1);
    }
    

    This requires no file to be written until you've successfully found the ideal compression ratio.

    This loop starts with an $image_quality of 100, then counts downward trying each new value on a temporary in-memory buffer. If gets down to trying 0 and the file would still be too big, it returns an error message. Otherwise, if it gets to a value that works, it reports that and saves the new file using that image quality setting.

    If you're feeling adventurous, you could implement a binary search algorithm to find the ideal compression ratio more quickly, but this quick-fix gives the same result, albeit possibly a tad slower.

    0 讨论(0)
提交回复
热议问题