|
|
@@ -0,0 +1,137 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace app\common\service;
|
|
|
+
|
|
|
+use Exception;
|
|
|
+
|
|
|
+class ImageCompressService
|
|
|
+{
|
|
|
+ /**
|
|
|
+ * 压缩图片
|
|
|
+ * @param string $sourcePath 源文件路径
|
|
|
+ * @param int $quality 压缩质量 (1-100)
|
|
|
+ * @param int $maxWidth 最大宽度
|
|
|
+ * @param int $maxHeight 最大高度
|
|
|
+ * @return string|false 压缩后的文件路径
|
|
|
+ */
|
|
|
+ public static function compress($sourcePath, $quality = 80, $maxWidth = 1920, $maxHeight = 1080)
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ // 获取图片信息
|
|
|
+ $imageInfo = getimagesize($sourcePath);
|
|
|
+ if (!$imageInfo) {
|
|
|
+ throw new Exception('无法获取图片信息');
|
|
|
+ }
|
|
|
+
|
|
|
+ $width = $imageInfo[0];
|
|
|
+ $height = $imageInfo[1];
|
|
|
+ $type = $imageInfo[2];
|
|
|
+
|
|
|
+ // 如果图片尺寸已经很小,且质量要求不高,直接返回原文件
|
|
|
+ if ($width <= $maxWidth && $height <= $maxHeight && $quality >= 90) {
|
|
|
+ return $sourcePath;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算新尺寸
|
|
|
+ $newSize = self::calculateNewSize($width, $height, $maxWidth, $maxHeight);
|
|
|
+ $newWidth = $newSize['width'];
|
|
|
+ $newHeight = $newSize['height'];
|
|
|
+
|
|
|
+ // 创建源图片资源
|
|
|
+ $sourceImage = self::createImageFromType($sourcePath, $type);
|
|
|
+ if (!$sourceImage) {
|
|
|
+ throw new Exception('不支持的图片格式');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建新图片资源
|
|
|
+ $newImage = imagecreatetruecolor($newWidth, $newHeight);
|
|
|
+
|
|
|
+ // 处理透明背景(PNG/GIF)
|
|
|
+ if ($type == IMAGETYPE_PNG || $type == IMAGETYPE_GIF) {
|
|
|
+ imagealphablending($newImage, false);
|
|
|
+ imagesavealpha($newImage, true);
|
|
|
+ $transparent = imagecolorallocatealpha($newImage, 255, 255, 255, 127);
|
|
|
+ imagefill($newImage, 0, 0, $transparent);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 重新采样
|
|
|
+ imagecopyresampled($newImage, $sourceImage, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
|
|
|
+
|
|
|
+ // 生成压缩后的文件路径
|
|
|
+ $compressedPath = self::generateCompressedPath($sourcePath);
|
|
|
+
|
|
|
+ // 保存压缩后的图片
|
|
|
+ $result = self::saveImageByType($newImage, $compressedPath, $type, $quality);
|
|
|
+
|
|
|
+ // 释放内存
|
|
|
+ imagedestroy($sourceImage);
|
|
|
+ imagedestroy($newImage);
|
|
|
+
|
|
|
+ return $result ? $compressedPath : false;
|
|
|
+
|
|
|
+ } catch (Exception $e) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算新尺寸(保持宽高比)
|
|
|
+ */
|
|
|
+ private static function calculateNewSize($width, $height, $maxWidth, $maxHeight)
|
|
|
+ {
|
|
|
+ $ratio = min($maxWidth / $width, $maxHeight / $height, 1);
|
|
|
+ return [
|
|
|
+ 'width' => intval($width * $ratio),
|
|
|
+ 'height' => intval($height * $ratio)
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据图片类型创建图片资源
|
|
|
+ */
|
|
|
+ private static function createImageFromType($path, $type)
|
|
|
+ {
|
|
|
+ switch ($type) {
|
|
|
+ case IMAGETYPE_JPEG:
|
|
|
+ return imagecreatefromjpeg($path);
|
|
|
+ case IMAGETYPE_PNG:
|
|
|
+ return imagecreatefrompng($path);
|
|
|
+ case IMAGETYPE_GIF:
|
|
|
+ return imagecreatefromgif($path);
|
|
|
+ case IMAGETYPE_WEBP:
|
|
|
+ return imagecreatefromwebp($path);
|
|
|
+ default:
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据图片类型保存图片
|
|
|
+ */
|
|
|
+ private static function saveImageByType($image, $path, $type, $quality)
|
|
|
+ {
|
|
|
+ switch ($type) {
|
|
|
+ case IMAGETYPE_JPEG:
|
|
|
+ return imagejpeg($image, $path, $quality);
|
|
|
+ case IMAGETYPE_PNG:
|
|
|
+ // PNG压缩级别 0-9,质量参数需要转换
|
|
|
+ $pngQuality = intval((100 - $quality) / 10);
|
|
|
+ return imagepng($image, $path, $pngQuality);
|
|
|
+ case IMAGETYPE_GIF:
|
|
|
+ return imagegif($image, $path);
|
|
|
+ case IMAGETYPE_WEBP:
|
|
|
+ return imagewebp($image, $path, $quality);
|
|
|
+ default:
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成压缩后的文件路径
|
|
|
+ */
|
|
|
+ private static function generateCompressedPath($originalPath)
|
|
|
+ {
|
|
|
+ $pathInfo = pathinfo($originalPath);
|
|
|
+ return $pathInfo['dirname'] . '/' . $pathInfo['filename'] . '_compressed.' . $pathInfo['extension'];
|
|
|
+ }
|
|
|
+}
|