perf: 优化图片压缩

This commit is contained in:
kuaifan
2023-06-15 16:41:10 +08:00
parent fd33eb3d1c
commit 29bbbd804a
12 changed files with 310 additions and 326 deletions

235
app/Module/Image.php Normal file
View File

@@ -0,0 +1,235 @@
<?php
namespace App\Module;
use Imagick;
/**
* 图片类:缩略图、调整大小、压缩
*/
class Image
{
private Imagick $image;
private string $path;
private int $height;
private int $width;
/**
* @param $imagePath
* @throws \ImagickException
*/
public function __construct($imagePath) {
$this->path = $imagePath;
$this->image = new Imagick($this->path);
$geo = $this->image->getImageGeometry();
$this->height = $geo['height'];
$this->width = $geo['width'];
}
/**
* 获取图片宽度
* @return int
*/
public function getWidth(): int
{
return $this->width;
}
/**
* 获取图片高度
* @return int
*/
public function getHeight(): int
{
return $this->height;
}
/**
* 创建缩略图
* @param int $width
* @param int $height
* @param string $mode = 'percentage|cover|contain' ($width 和 $height 都设置的情况下有效)
* @return Image
* @throws \ImagickException
*/
public function thumb(int $width, int $height, string $mode = 'percentage'): static
{
if ($height === 0 && $width === 0) {
return $this;
}
if ($width && $height) {
if ($mode === 'cover') {
// 铺满背景(超出被裁掉)
$this->image->cropThumbnailImage($width, $height);
} else {
// 等比缩放
if ($this->width >= $this->height) {
$this->image->thumbnailImage($width, 0);
} else {
$this->image->thumbnailImage(0, $height);
}
if ($mode === 'contain') {
// 显示完整的图
$background = new Imagick();
$background->newImage($width, $height, 'none', strtolower(pathinfo($this->path, PATHINFO_EXTENSION)));
$bg_width = $background->getImageWidth();
$bg_height = $background->getImageHeight();
$img_width = $this->image->getImageWidth();
$img_height = $this->image->getImageHeight();
if ($img_width / $bg_width > $img_height / $bg_height) {
$width = $bg_width;
$height = intval($img_height / ($img_width / $width));
} else {
$height = $bg_height;
$width = intval($img_width / ($img_height / $height));
}
$this->image->resizeImage($width, $height, Imagick::FILTER_LANCZOS, 1);
$x = intval(($bg_width - $width) / 2);
$y = intval(($bg_height - $height) / 2);
$background->compositeImage($this->image, Imagick::COMPOSITE_DEFAULT, $x, $y);
$this->image->destroy();
$this->image = $background;
}
}
} else {
$this->image->thumbnailImage($width, $height);
}
return $this;
}
/**
* 调整图像大小
* @param int $width
* @param int $height
* @return Image
* @throws \ImagickException
*/
public function resize(int $width, int $height): static
{
if ($height === 0 && $width === 0) {
return $this;
}
$this->image->adaptiveResizeImage($width, $height);
return $this;
}
/**
* 压缩图片
* @param int $quality
* @return $this
* @throws \ImagickException
*/
public function compress(int $quality = 100): static
{
$format = strtolower($this->image->getImageFormat());
switch ($format) {
case 'jpeg':
case 'jpg':
$this->image->setImageCompression(Imagick::COMPRESSION_JPEG);
break;
case 'png':
$this->image->setImageCompression(Imagick::COMPRESSION_ZIP);
break;
case 'gif':
$this->image->setImageCompression(Imagick::COMPRESSION_LZW);
break;
default:
return $this;
}
if ($quality > 1) {
$this->image->setImageCompressionQuality($quality);
} elseif ($quality > 0) {
$this->image->setImageCompressionQuality($quality * 100);
} else {
$this->image->setImageCompressionQuality(100);
}
return $this;
}
/**
* 保存结果到文件
* @param string $savePath Save path
* @return void
* @throws \ImagickException
*/
public function saveTo(string $savePath): void
{
$this->image->writeImage($savePath);
$this->image->destroy();
}
/** ******************************************************************************/
/** ******************************************************************************/
/** ******************************************************************************/
/**
* 生成缩略图
* @param string $imagePath 图片路径
* @param string $savePath 保存路径
* @param int $width 宽度
* @param int $height 高度
* @param string $mode 模式percentage|cover|contain
* @return string|null 成功返回图片后缀,失败返回 false
*/
public static function thumbImage(string $imagePath, string $savePath, int $width, int $height, string $mode = 'percentage'): ?string
{
if (!file_exists($imagePath)) {
return null;
}
try {
$extension = strtolower(pathinfo($imagePath, PATHINFO_EXTENSION));
if (str_contains($savePath, '.{*}')) {
$savePath = str_replace('.{*}', '.' . $extension, $savePath);
}
$image = new Image($imagePath);
$image->thumb($width, $height, $mode);
$image->saveTo($savePath);
return $extension;
} catch (\ImagickException) {
return null;
}
}
/**
* 压缩图片
* @param string $imagePath 图片路径
* @param string|null $savePath 保存路径(默认覆盖原图)
* @param int $quality 压缩质量0-100
* @param float $minSize 最小尺寸单位KB
* @return bool
*/
public static function compressImage(string $imagePath, string $savePath = null, int $quality = 100, float $minSize = 10): bool
{
if (Base::settingFind("system", "image_compress") === 'close') {
return false;
}
if (!file_exists($imagePath)) {
return false;
}
$imageSize = filesize($imagePath);
if ($minSize > 0 && $imageSize < $minSize * 1024) {
return false;
}
if (empty($savePath)) {
$savePath = $imagePath;
}
$tmpPath = $imagePath . '.compress.tmp';
try {
$image = new Image($imagePath);
$image->compress($quality);
$image->saveTo($tmpPath);
if (filesize($tmpPath) >= $imageSize) {
copy($imagePath, $savePath);
unlink($tmpPath);
} else {
rename($tmpPath, $savePath);
}
return true;
} catch (\ImagickException) {
return false;
}
}
}