在 PHP 中高效调整 JPEG 图像大小

发布于 2024-07-04 05:35:48 字数 1635 浏览 5 评论 0原文

在 PHP 中调整大图像大小最有效的方法是什么?

我目前正在使用 GD 函数 imagecopyresampled 来拍摄高分辨率图像,并将其大小调整为用于 Web 查看的尺寸(大约 700 像素宽 x 700 像素高)。

这对于小照片(小于 2 MB)非常有效,并且整个调整大小操作在服务器上花费的时间不到一秒钟。 然而,该网站最终将为可能上传尺寸高达 10 MB 的图像(或尺寸高达 5000x4000 像素的图像)的摄影师提供服务。

对大图像执行这种大小调整操作往往会大幅增加内存使用量(较大的图像可能会使脚本的内存使用量超过 80 MB)。 有什么办法可以让这个调整大小操作更加高效吗? 我应该使用备用图像库,例如 ImageMagick 吗?

现在,调整大小代码看起来像这样

function makeThumbnail($sourcefile, $endfile, $thumbwidth, $thumbheight, $quality) {
    // Takes the sourcefile (path/to/image.jpg) and makes a thumbnail from it
    // and places it at endfile (path/to/thumb.jpg).

    // Load image and get image size.
    $img = imagecreatefromjpeg($sourcefile);
    $width = imagesx( $img );
    $height = imagesy( $img );

    if ($width > $height) {
        $newwidth = $thumbwidth;
        $divisor = $width / $thumbwidth;
        $newheight = floor( $height / $divisor);
    } else {
        $newheight = $thumbheight;
        $divisor = $height / $thumbheight;
        $newwidth = floor( $width / $divisor );
    }

    // Create a new temporary image.
    $tmpimg = imagecreatetruecolor( $newwidth, $newheight );

    // Copy and resize old image into new image.
    imagecopyresampled( $tmpimg, $img, 0, 0, 0, 0, $newwidth, $newheight, $width, $height );

    // Save thumbnail into a file.
    imagejpeg( $tmpimg, $endfile, $quality);

    // release the memory
    imagedestroy($tmpimg);
    imagedestroy($img);

What's the most efficient way to resize large images in PHP?

I'm currently using the GD function imagecopyresampled to take high resolution images, and cleanly resize them down to a size for web viewing (roughly 700 pixels wide by 700 pixels tall).

This works great on small (under 2 MB) photos and the entire resize operation takes less than a second on the server. However, the site will eventually service photographers who may be uploading images up to 10 MB in size (or images up to 5000x4000 pixels in size).

Doing this kind of resize operation with large images tends to increase the memory usage by a very large margin (larger images can spike the memory usage for the script past 80 MB). Is there any way to make this resize operation more efficient? Should I be using an alternate image library such as ImageMagick?

Right now, the resize code looks something like this

function makeThumbnail($sourcefile, $endfile, $thumbwidth, $thumbheight, $quality) {
    // Takes the sourcefile (path/to/image.jpg) and makes a thumbnail from it
    // and places it at endfile (path/to/thumb.jpg).

    // Load image and get image size.
    $img = imagecreatefromjpeg($sourcefile);
    $width = imagesx( $img );
    $height = imagesy( $img );

    if ($width > $height) {
        $newwidth = $thumbwidth;
        $divisor = $width / $thumbwidth;
        $newheight = floor( $height / $divisor);
    } else {
        $newheight = $thumbheight;
        $divisor = $height / $thumbheight;
        $newwidth = floor( $width / $divisor );
    }

    // Create a new temporary image.
    $tmpimg = imagecreatetruecolor( $newwidth, $newheight );

    // Copy and resize old image into new image.
    imagecopyresampled( $tmpimg, $img, 0, 0, 0, 0, $newwidth, $newheight, $width, $height );

    // Save thumbnail into a file.
    imagejpeg( $tmpimg, $endfile, $quality);

    // release the memory
    imagedestroy($tmpimg);
    imagedestroy($img);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

岁月打碎记忆 2024-07-11 05:35:49

我建议您按照以下方式进行操作:

  1. 对上传的文件执行 getimagesize( ) 以检查图像类型和大小
  2. 将任何上传的小于 700x700px 的 JPEG 图像“按原样”保存到目标文件夹中
  3. 使用 GD 库处理中等大小的图像(有关代码示例,请参阅本文:调整图像大小使用 PHP 和 GD 库)
  4. 使用 ImageMagick 处理大图像。 如果您愿意,可以在后台使用 ImageMagick。

要在后台使用 ImageMagick,请将上传的文件移动到临时文件夹并安排一个 CRON 作业,将所有文件“转换”为 jpeg 并相应地调整它们的大小。 请参阅命令语法: imagemagick-命令行处理

您可以提示用户该文件已上传并计划进行处理。 CRON 作业可以安排每天以特定的时间间隔运行。 处理后可以删除源图像,以确保图像不会被处理两次。

I suggest that you work something along these lines:

  1. Perform a getimagesize( ) on the uploaded file to check image type and size
  2. Save any uploaded JPEG image smaller than 700x700px in to the destination folder "as-is"
  3. Use GD library for medium size images (see this article for code sample: Resize Images Using PHP and GD Library)
  4. Use ImageMagick for large images. You can use ImageMagick in background if you prefer.

To use ImageMagick in background, move the uploaded files to a temporary folder and schedule a CRON job that "convert"s all files to jpeg and resizes them accordingly. See command syntax at: imagemagick-command line processing

You can prompt the user that file is uploaded and scheduled to be processed. The CRON job could be scheduled to run daily at a specific interval. The source image could be deleted after processing to assure that an image is not processed twice.

凹づ凸ル 2024-07-11 05:35:49

我听说过有关 Imagick 库的大事,不幸的是我无法在我的工作计算机上安装它,也无法在家里安装它(相信我,我在各种论坛上花了很多时间)。

之后,我决定尝试这个 PHP 类:

http://www.verot.net/php_class_upload.htm

这非常酷,我可以调整各种图像的大小(我也可以将它们转换为 JPG)。

I've heard big things about the Imagick library, unfortunately I couldn't install it at my work computer and neither at home (and trust me, I spent hours and hours on all kinds of forums).

Afterwords, I've decided to try this PHP class:

http://www.verot.net/php_class_upload.htm

It's pretty cool and I can resize all kinds of images (I can convert them to JPG also).

天邊彩虹 2024-07-11 05:35:49

ImageMagick是多线程的,因此看起来更快,但实际上比GD使用更多的资源。 如果您使用 GD 并行运行多个 PHP 脚本,那么它们在简单操作的速度上会击败 ImageMagick。 ExactImage 的功能不如 ImageMagick,但速度要快得多,尽管无法通过 PHP 获得,但您必须将其安装在服务器上并通过 exec 运行它。

ImageMagick is multithreaded, so it appears to be faster, but actually uses a lot more resources than GD. If you ran several PHP scripts in parallel all using GD then they'd beat ImageMagick in speed for simple operations. ExactImage is less powerful than ImageMagick but a lot faster, though not available through PHP, you'll have to install it on the server and run it through exec.

坦然微笑 2024-07-11 05:35:49

对于较大的图像,请使用 phpThumb()。 以下是如何使用它: http: //abcoder.com/php/problem-with-resizing-corrupted-images-using-php-image-functions/。 它也适用于大型损坏的图像。

For larger images use phpThumb(). Here is how to use it: http://abcoder.com/php/problem-with-resizing-corrupted-images-using-php-image-functions/. It also works for large corrupted images.

佼人 2024-07-11 05:35:49

人们说 ImageMagick 速度更快。 充其量只是比较两个库并进行测量。

  1. 准备1000张典型图像。
  2. 写两个脚本——一个用于GD,一个
    对于 ImageMagick。
  3. 将它们都运行几次。
  4. 比较结果(总执行
    时间、CPU 和 I/O 使用情况、结果
    画面质量)。

对其他人来说最好的东西,不一定对你来说是最好的。

另外,在我看来,ImageMagick 有更好的 API 接口。

People say that ImageMagick is much faster. At best just compare both libraries and measure that.

  1. Prepare 1000 typical images.
  2. Write two scripts -- one for GD, one
    for ImageMagick.
  3. Run both of them a few times.
  4. Compare results (total execution
    time, CPU and I/O usage, result
    image quality).

Something which the best everyone else, could not be the best for you.

Also, in my opinion, ImageMagick has much better API interface.

扛刀软妹 2024-07-11 05:35:49

这是我在项目中使用的 php.net 文档的片段,运行良好:

<?
function fastimagecopyresampled (&$dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h, $quality = 3) {
    // Plug-and-Play fastimagecopyresampled function replaces much slower imagecopyresampled.
    // Just include this function and change all "imagecopyresampled" references to "fastimagecopyresampled".
    // Typically from 30 to 60 times faster when reducing high resolution images down to thumbnail size using the default quality setting.
    // Author: Tim Eckel - Date: 09/07/07 - Version: 1.1 - Project: FreeRingers.net - Freely distributable - These comments must remain.
    //
    // Optional "quality" parameter (defaults is 3). Fractional values are allowed, for example 1.5. Must be greater than zero.
    // Between 0 and 1 = Fast, but mosaic results, closer to 0 increases the mosaic effect.
    // 1 = Up to 350 times faster. Poor results, looks very similar to imagecopyresized.
    // 2 = Up to 95 times faster.  Images appear a little sharp, some prefer this over a quality of 3.
    // 3 = Up to 60 times faster.  Will give high quality smooth results very close to imagecopyresampled, just faster.
    // 4 = Up to 25 times faster.  Almost identical to imagecopyresampled for most images.
    // 5 = No speedup. Just uses imagecopyresampled, no advantage over imagecopyresampled.

    if (empty($src_image) || empty($dst_image) || $quality <= 0) { return false; }
    if ($quality < 5 && (($dst_w * $quality) < $src_w || ($dst_h * $quality) < $src_h)) {
        $temp = imagecreatetruecolor ($dst_w * $quality + 1, $dst_h * $quality + 1);
        imagecopyresized ($temp, $src_image, 0, 0, $src_x, $src_y, $dst_w * $quality + 1, $dst_h * $quality + 1, $src_w, $src_h);
        imagecopyresampled ($dst_image, $temp, $dst_x, $dst_y, 0, 0, $dst_w, $dst_h, $dst_w * $quality, $dst_h * $quality);
        imagedestroy ($temp);
    } else imagecopyresampled ($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
    return true;
}
?>

http://us.php.net/manual/en/function.imagecopyresampled.php#77679

Here's a snippet from the php.net docs that I've used in a project and works fine:

<?
function fastimagecopyresampled (&$dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h, $quality = 3) {
    // Plug-and-Play fastimagecopyresampled function replaces much slower imagecopyresampled.
    // Just include this function and change all "imagecopyresampled" references to "fastimagecopyresampled".
    // Typically from 30 to 60 times faster when reducing high resolution images down to thumbnail size using the default quality setting.
    // Author: Tim Eckel - Date: 09/07/07 - Version: 1.1 - Project: FreeRingers.net - Freely distributable - These comments must remain.
    //
    // Optional "quality" parameter (defaults is 3). Fractional values are allowed, for example 1.5. Must be greater than zero.
    // Between 0 and 1 = Fast, but mosaic results, closer to 0 increases the mosaic effect.
    // 1 = Up to 350 times faster. Poor results, looks very similar to imagecopyresized.
    // 2 = Up to 95 times faster.  Images appear a little sharp, some prefer this over a quality of 3.
    // 3 = Up to 60 times faster.  Will give high quality smooth results very close to imagecopyresampled, just faster.
    // 4 = Up to 25 times faster.  Almost identical to imagecopyresampled for most images.
    // 5 = No speedup. Just uses imagecopyresampled, no advantage over imagecopyresampled.

    if (empty($src_image) || empty($dst_image) || $quality <= 0) { return false; }
    if ($quality < 5 && (($dst_w * $quality) < $src_w || ($dst_h * $quality) < $src_h)) {
        $temp = imagecreatetruecolor ($dst_w * $quality + 1, $dst_h * $quality + 1);
        imagecopyresized ($temp, $src_image, 0, 0, $src_x, $src_y, $dst_w * $quality + 1, $dst_h * $quality + 1, $src_w, $src_h);
        imagecopyresampled ($dst_image, $temp, $dst_x, $dst_y, 0, 0, $dst_w, $dst_h, $dst_w * $quality, $dst_h * $quality);
        imagedestroy ($temp);
    } else imagecopyresampled ($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
    return true;
}
?>

http://us.php.net/manual/en/function.imagecopyresampled.php#77679

哥,最终变帅啦 2024-07-11 05:35:49

phpThumb 尽可能使用 ImageMagick 来提高速度(如果需要,则回退到 GD),并且似乎缓存得很好,以减少加载到服务器上。 尝试起来非常轻量(要调整图像大小,只需使用包含图形文件名和输出尺寸的 GET 查询调用 phpThumb.php),因此您可以尝试一下,看看它是否满足您的需求。

phpThumb uses ImageMagick whenever possible for speed (falling back to GD if necessary) and seems to cache pretty well to reduce the load on the server. It's pretty lightweight to try out (to resize an image, just call phpThumb.php with a GET query that includes the graphic filename and output dimensions), so you might give it a shot to see if it meets your needs.

吻安 2024-07-11 05:35:49

对于较大的图像,使用 libjpeg 在 ImageMagick 中调整图像加载大小,从而显着减少内存使用并提高性能,这是 GD 无法实现的。

$im = new Imagick();
try {
  $im->pingImage($file_name);
} catch (ImagickException $e) {
  throw new Exception(_('Invalid or corrupted image file, please try uploading another image.'));
}

$width  = $im->getImageWidth();
$height = $im->getImageHeight();
if ($width > $config['width_threshold'] || $height > $config['height_threshold'])
{
  try {
/* send thumbnail parameters to Imagick so that libjpeg can resize images
 * as they are loaded instead of consuming additional resources to pass back
 * to PHP.
 */
    $fitbyWidth = ($config['width_threshold'] / $width) > ($config['height_threshold'] / $height);
    $aspectRatio = $height / $width;
    if ($fitbyWidth) {
      $im->setSize($config['width_threshold'], abs($width * $aspectRatio));
    } else {
      $im->setSize(abs($height / $aspectRatio), $config['height_threshold']);
    }
    $im->readImage($file_name);

/* Imagick::thumbnailImage(fit = true) has a bug that it does fit both dimensions
 */
//  $im->thumbnailImage($config['width_threshold'], $config['height_threshold'], true);

// workaround:
    if ($fitbyWidth) {
      $im->thumbnailImage($config['width_threshold'], 0, false);
    } else {
      $im->thumbnailImage(0, $config['height_threshold'], false);
    }

    $im->setImageFileName($thumbnail_name);
    $im->writeImage();
  }
  catch (ImagickException $e)
  {
    header('HTTP/1.1 500 Internal Server Error');
    throw new Exception(_('An error occured reszing the image.'));
  }
}

/* cleanup Imagick
 */
$im->destroy();

For larger images use libjpeg to resize on image load in ImageMagick and thereby significantly reducing memory usage and improving performance, it is not possible with GD.

$im = new Imagick();
try {
  $im->pingImage($file_name);
} catch (ImagickException $e) {
  throw new Exception(_('Invalid or corrupted image file, please try uploading another image.'));
}

$width  = $im->getImageWidth();
$height = $im->getImageHeight();
if ($width > $config['width_threshold'] || $height > $config['height_threshold'])
{
  try {
/* send thumbnail parameters to Imagick so that libjpeg can resize images
 * as they are loaded instead of consuming additional resources to pass back
 * to PHP.
 */
    $fitbyWidth = ($config['width_threshold'] / $width) > ($config['height_threshold'] / $height);
    $aspectRatio = $height / $width;
    if ($fitbyWidth) {
      $im->setSize($config['width_threshold'], abs($width * $aspectRatio));
    } else {
      $im->setSize(abs($height / $aspectRatio), $config['height_threshold']);
    }
    $im->readImage($file_name);

/* Imagick::thumbnailImage(fit = true) has a bug that it does fit both dimensions
 */
//  $im->thumbnailImage($config['width_threshold'], $config['height_threshold'], true);

// workaround:
    if ($fitbyWidth) {
      $im->thumbnailImage($config['width_threshold'], 0, false);
    } else {
      $im->thumbnailImage(0, $config['height_threshold'], false);
    }

    $im->setImageFileName($thumbnail_name);
    $im->writeImage();
  }
  catch (ImagickException $e)
  {
    header('HTTP/1.1 500 Internal Server Error');
    throw new Exception(_('An error occured reszing the image.'));
  }
}

/* cleanup Imagick
 */
$im->destroy();
沒落の蓅哖 2024-07-11 05:35:49

从你的问题来看,你似乎对GD有点陌生,我分享一下我的一些经验,
也许这有点偏离主题,但我认为这对像您这样刚接触 GD 的人会有帮助:

第 1 步,验证文件。使用以下函数检查 $_FILES[ 'image']['tmp_name'] 文件是有效文件:

   function getContentsFromImage($image) {
      if (@is_file($image) == true) {
         return file_get_contents($image);
      } else {
         throw new \Exception('Invalid image');
      }
   }
   $contents = getContentsFromImage($_FILES['image']['tmp_name']);

第 2 步,获取文件格式 尝试使用以下带有 finfo 扩展名的函数来检查文件(内容)的文件格式。 你可能会说为什么不直接使用 $_FILES["image"]["type"] 来检查文件格式呢? 因为它检查文件扩展名而不是文件内容,所以如果有人将最初名为 world.png 的文件重命名为 world.jpg$ _FILES["image"]["type"] 将返回 jpeg 而不是 png,因此 $_FILES["image"]["type"] 可能会返回错误的结果。

   function getFormatFromContents($contents) {
      $finfo = new \finfo();
      $mimetype = $finfo->buffer($contents, FILEINFO_MIME_TYPE);
      switch ($mimetype) {
         case 'image/jpeg':
            return 'jpeg';
            break;
         case 'image/png':
            return 'png';
            break;
         case 'image/gif':
            return 'gif';
            break;
         default:
            throw new \Exception('Unknown or unsupported image format');
      }
   }
   $format = getFormatFromContents($contents);

第三步,获取GD资源 从我们之前的内容中获取GD资源:

   function getGDResourceFromContents($contents) {
      $resource = @imagecreatefromstring($contents);
      if ($resource == false) {
         throw new \Exception('Cannot process image');
      }
      return $resource;
   }
   $resource = getGDResourceFromContents($contents);

第四步,获取图像尺寸 现在您可以通过以下简单的代码获取图像尺寸:

  $width = imagesx($resource);
  $height = imagesy($resource);

>现在,让我们看看我们从原始图像中得到了什么变量:

       $contents, $format, $resource, $width, $height
       OK, lets move on

第5步,计算调整大小的图像参数这一步与你的问题相关,以下函数的目的是得到调整GD函数imagecopyresampled()的参数大小,代码有点长,但是效果很好,甚至有拉伸、收缩和填充三个选项。

拉伸:输出图像的尺寸与您设置的新尺寸相同。 不会保持高度/宽度比。

收缩:输出图像的尺寸不会超过您指定的新尺寸,并保持图像的高宽比。

填充:输出图像的尺寸将与您指定的新尺寸相同,它将裁剪并填充图像。 如果需要,调整图像大小,并保持图像高/宽比。 此选项是您问题中所需要的。

   function getResizeArgs($width, $height, $newwidth, $newheight, $option) {
      if ($option === 'stretch') {
         if ($width === $newwidth && $height === $newheight) {
            return false;
         }
         $dst_w = $newwidth;
         $dst_h = $newheight;
         $src_w = $width;
         $src_h = $height;
         $src_x = 0;
         $src_y = 0;
      } else if ($option === 'shrink') {
         if ($width <= $newwidth && $height <= $newheight) {
            return false;
         } else if ($width / $height >= $newwidth / $newheight) {
            $dst_w = $newwidth;
            $dst_h = (int) round(($newwidth * $height) / $width);
         } else {
            $dst_w = (int) round(($newheight * $width) / $height);
            $dst_h = $newheight;
         }
         $src_x = 0;
         $src_y = 0;
         $src_w = $width;
         $src_h = $height;
      } else if ($option === 'fill') {
         if ($width === $newwidth && $height === $newheight) {
            return false;
         }
         if ($width / $height >= $newwidth / $newheight) {
            $src_w = (int) round(($newwidth * $height) / $newheight);
            $src_h = $height;
            $src_x = (int) round(($width - $src_w) / 2);
            $src_y = 0;
         } else {
            $src_w = $width;
            $src_h = (int) round(($width * $newheight) / $newwidth);
            $src_x = 0;
            $src_y = (int) round(($height - $src_h) / 2);
         }
         $dst_w = $newwidth;
         $dst_h = $newheight;
      }
      if ($src_w < 1 || $src_h < 1) {
         throw new \Exception('Image width or height is too small');
      }
      return array(
          'dst_x' => 0,
          'dst_y' => 0,
          'src_x' => $src_x,
          'src_y' => $src_y,
          'dst_w' => $dst_w,
          'dst_h' => $dst_h,
          'src_w' => $src_w,
          'src_h' => $src_h
      );
   }
   $args = getResizeArgs($width, $height, 150, 170, 'fill');

第 6 步,调整图像大小 使用 $args$width、我们从上面得到的 $height$format 和 $resource 放入以下函数中,并获取调整大小后的图像的新资源:

   function runResize($width, $height, $format, $resource, $args) {
      if ($args === false) {
         return; //if $args equal to false, this means no resize occurs;
      }
      $newimage = imagecreatetruecolor($args['dst_w'], $args['dst_h']);
      if ($format === 'png') {
         imagealphablending($newimage, false);
         imagesavealpha($newimage, true);
         $transparentindex = imagecolorallocatealpha($newimage, 255, 255, 255, 127);
         imagefill($newimage, 0, 0, $transparentindex);
      } else if ($format === 'gif') {
         $transparentindex = imagecolorallocatealpha($newimage, 255, 255, 255, 127);
         imagefill($newimage, 0, 0, $transparentindex);
         imagecolortransparent($newimage, $transparentindex);
      }
      imagecopyresampled($newimage, $resource, $args['dst_x'], $args['dst_y'], $args['src_x'], $args['src_y'], $args['dst_w'], $args['dst_h'], $args['src_w'], $args['src_h']);
      imagedestroy($resource);
      return $newimage;
   }
   $newresource = runResize($width, $height, $format, $resource, $args);

第 7 步,获取新内容< /strong>,使用以下函数从新的GD资源中获取内容:

   function getContentsFromGDResource($resource, $format) {
      ob_start();
      switch ($format) {
         case 'gif':
            imagegif($resource);
            break;
         case 'jpeg':
            imagejpeg($resource, NULL, 100);
            break;
         case 'png':
            imagepng($resource, NULL, 9);
      }
      $contents = ob_get_contents();
      ob_end_clean();
      return $contents;
   }
   $newcontents = getContentsFromGDResource($newresource, $format);

第8步获取扩展名,使用以下函数获取图像格式的扩展名(注意,图像格式不等于图像)扩展名):

   function getExtensionFromFormat($format) {
      switch ($format) {
         case 'gif':
            return 'gif';
            break;
         case 'jpeg':
            return 'jpg';
            break;
         case 'png':
            return 'png';
      }
   }
   $extension = getExtensionFromFormat($format);

第 9 步保存图像 如果我们有一个名为 mike 的用户,您可以执行以下操作,它将保存到与此 php 脚本相同的文件夹中:

$user_name = 'mike';
$filename = $user_name . '.' . $extension;
file_put_contents($filename, $newcontents);

第 10 步销毁资源 > 不要忘记破坏GD资源!

imagedestroy($newresource);

或者你可以将所有代码写到一个类中,然后简单地使用以下内容:

   public function __destruct() {
      @imagedestroy($this->resource);
   }

TIPS

我建议不要转换用户上传的文件格式,你会遇到很多问题。

From you quesion, it seems you are kinda new to GD, I will share some experence of mine,
maybe this is a bit off topic, but I think it will be helpful to someone new to GD like you:

Step 1, validate file. Use the following function to check if the $_FILES['image']['tmp_name'] file is valid file:

   function getContentsFromImage($image) {
      if (@is_file($image) == true) {
         return file_get_contents($image);
      } else {
         throw new \Exception('Invalid image');
      }
   }
   $contents = getContentsFromImage($_FILES['image']['tmp_name']);

Step 2, get file format Try the following function with finfo extension to check file format of the file(contents). You would say why don't you just use $_FILES["image"]["type"] to check file format? Because it ONLY check file extension not file contents, if someone rename a file originally called world.png to world.jpg, $_FILES["image"]["type"] will return jpeg not png, so $_FILES["image"]["type"] may return wrong result.

   function getFormatFromContents($contents) {
      $finfo = new \finfo();
      $mimetype = $finfo->buffer($contents, FILEINFO_MIME_TYPE);
      switch ($mimetype) {
         case 'image/jpeg':
            return 'jpeg';
            break;
         case 'image/png':
            return 'png';
            break;
         case 'image/gif':
            return 'gif';
            break;
         default:
            throw new \Exception('Unknown or unsupported image format');
      }
   }
   $format = getFormatFromContents($contents);

Step.3, Get GD resource Get GD resource from contents we have before:

   function getGDResourceFromContents($contents) {
      $resource = @imagecreatefromstring($contents);
      if ($resource == false) {
         throw new \Exception('Cannot process image');
      }
      return $resource;
   }
   $resource = getGDResourceFromContents($contents);

Step 4, get image dimension Now you can get image dimension with the following simple code:

  $width = imagesx($resource);
  $height = imagesy($resource);

Now, Let's see what variable we got from the original image then:

       $contents, $format, $resource, $width, $height
       OK, lets move on

Step 5, calculate resized image arguments This step is related to your question, the purpose of the following function is to get resize arguments for GD function imagecopyresampled(), the code is kinda long, but it works great, it even has three options: stretch, shrink, and fill.

stretch: output image's dimension is the same as the new dimension you set. Won't keep height/width ratio.

shrink: output image's dimension won't exceed the new dimension you give, and keep image height/width ratio.

fill: output image's dimension will be the same as new dimension you give, it will crop & resize image if needed, and keep image height/width ratio. This option is what you need in your question.

   function getResizeArgs($width, $height, $newwidth, $newheight, $option) {
      if ($option === 'stretch') {
         if ($width === $newwidth && $height === $newheight) {
            return false;
         }
         $dst_w = $newwidth;
         $dst_h = $newheight;
         $src_w = $width;
         $src_h = $height;
         $src_x = 0;
         $src_y = 0;
      } else if ($option === 'shrink') {
         if ($width <= $newwidth && $height <= $newheight) {
            return false;
         } else if ($width / $height >= $newwidth / $newheight) {
            $dst_w = $newwidth;
            $dst_h = (int) round(($newwidth * $height) / $width);
         } else {
            $dst_w = (int) round(($newheight * $width) / $height);
            $dst_h = $newheight;
         }
         $src_x = 0;
         $src_y = 0;
         $src_w = $width;
         $src_h = $height;
      } else if ($option === 'fill') {
         if ($width === $newwidth && $height === $newheight) {
            return false;
         }
         if ($width / $height >= $newwidth / $newheight) {
            $src_w = (int) round(($newwidth * $height) / $newheight);
            $src_h = $height;
            $src_x = (int) round(($width - $src_w) / 2);
            $src_y = 0;
         } else {
            $src_w = $width;
            $src_h = (int) round(($width * $newheight) / $newwidth);
            $src_x = 0;
            $src_y = (int) round(($height - $src_h) / 2);
         }
         $dst_w = $newwidth;
         $dst_h = $newheight;
      }
      if ($src_w < 1 || $src_h < 1) {
         throw new \Exception('Image width or height is too small');
      }
      return array(
          'dst_x' => 0,
          'dst_y' => 0,
          'src_x' => $src_x,
          'src_y' => $src_y,
          'dst_w' => $dst_w,
          'dst_h' => $dst_h,
          'src_w' => $src_w,
          'src_h' => $src_h
      );
   }
   $args = getResizeArgs($width, $height, 150, 170, 'fill');

Step 6, resize image Use $args, $width, $height, $format and $resource we got from above into the following function and get the new resource of the resized image:

   function runResize($width, $height, $format, $resource, $args) {
      if ($args === false) {
         return; //if $args equal to false, this means no resize occurs;
      }
      $newimage = imagecreatetruecolor($args['dst_w'], $args['dst_h']);
      if ($format === 'png') {
         imagealphablending($newimage, false);
         imagesavealpha($newimage, true);
         $transparentindex = imagecolorallocatealpha($newimage, 255, 255, 255, 127);
         imagefill($newimage, 0, 0, $transparentindex);
      } else if ($format === 'gif') {
         $transparentindex = imagecolorallocatealpha($newimage, 255, 255, 255, 127);
         imagefill($newimage, 0, 0, $transparentindex);
         imagecolortransparent($newimage, $transparentindex);
      }
      imagecopyresampled($newimage, $resource, $args['dst_x'], $args['dst_y'], $args['src_x'], $args['src_y'], $args['dst_w'], $args['dst_h'], $args['src_w'], $args['src_h']);
      imagedestroy($resource);
      return $newimage;
   }
   $newresource = runResize($width, $height, $format, $resource, $args);

Step 7, get new contents, Use the following function to get contents from the new GD resource:

   function getContentsFromGDResource($resource, $format) {
      ob_start();
      switch ($format) {
         case 'gif':
            imagegif($resource);
            break;
         case 'jpeg':
            imagejpeg($resource, NULL, 100);
            break;
         case 'png':
            imagepng($resource, NULL, 9);
      }
      $contents = ob_get_contents();
      ob_end_clean();
      return $contents;
   }
   $newcontents = getContentsFromGDResource($newresource, $format);

Step 8 get extension, Use the following function to get extension of from image format(note, image format is not equal to image extension):

   function getExtensionFromFormat($format) {
      switch ($format) {
         case 'gif':
            return 'gif';
            break;
         case 'jpeg':
            return 'jpg';
            break;
         case 'png':
            return 'png';
      }
   }
   $extension = getExtensionFromFormat($format);

Step 9 save image If we have a user named mike, you can do the following, it will save to the same folder as this php script:

$user_name = 'mike';
$filename = $user_name . '.' . $extension;
file_put_contents($filename, $newcontents);

Step 10 destroy resource Don't forget destroy GD resource!

imagedestroy($newresource);

or you can write all your code into a class, and simply use the following:

   public function __destruct() {
      @imagedestroy($this->resource);
   }

TIPS

I recommend not to convert file format that user upload, you will meet many problems.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文