Files
i-love/app/Console/Commands/OptimizeImageCompression.php
2025-06-17 10:21:03 +08:00

153 lines
4.9 KiB
PHP

<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
use App\Service\ImageService;
class OptimizeImageCompression extends Command
{
protected $signature = 'images:optimize-compression
{--max-size=1024 : 目标最大文件大小 (KB)}
{--quality=85 : 初始压缩质量}
{--min-quality=40 : 最低压缩质量}
{--max-iterations=10 : 最大压缩次数}';
protected $description = '优化压缩目录下的图片,压缩到指定大小以下';
public function handle()
{
// 不限制内存
ini_set('memory_limit', -1);
$disk = Storage::disk('public');
$directory = 'images/compressed';
$maxSizeKB = (int)$this->option('max-size');
$maxSizeBytes = $maxSizeKB * 1024;
$this->info("正在优化 {$directory} 目录下的图片...");
$this->info("目标文件大小: {$maxSizeKB}KB");
$images = $disk->allFiles($directory);
$total = count($images);
$count = 0;
$progressBar = $this->output->createProgressBar($total);
$progressBar->start();
foreach ($images as $imagePath) {
$originalSize = $disk->size($imagePath);
$fileExtension = pathinfo($imagePath, PATHINFO_EXTENSION);
// 只处理WEBP格式文件
if (strtolower($fileExtension) !== 'webp') {
$this->warn("跳过非WEBP文件: $imagePath");
$progressBar->advance();
continue;
}
// 如果已经小于目标大小,则跳过
if ($originalSize <= $maxSizeBytes) {
$progressBar->advance();
continue;
}
// 获取文件的绝对路径
$filePath = $disk->path($imagePath);
// 创建压缩实例
$imageService = app(ImageService::class);
$tempPath = storage_path('app/temp/'.basename($imagePath));
// 创建目标目录
if (!file_exists(dirname($tempPath))) {
mkdir(dirname($tempPath), 0755, true);
}
// 多轮压缩策略
$currentSize = $originalSize;
$iterations = 0;
$currentQuality = (int)$this->option('quality');
$minQuality = (int)$this->option('min-quality');
$maxIterations = (int)$this->option('max-iterations');
while ($currentSize > $maxSizeBytes && $iterations < $maxIterations && $currentQuality >= $minQuality) {
// 使用服务类进行压缩
$optimizedContent = $this->recompressImage(
$filePath,
$currentQuality
);
file_put_contents($tempPath, $optimizedContent);
$newSize = filesize($tempPath);
// 如果新文件大小没有减小,则停止优化
if ($newSize >= $currentSize) {
$this->warn("无法进一步压缩: $imagePath (质量: $currentQuality)");
break;
}
$currentSize = $newSize;
$iterations++;
// 降低质量进行下一轮尝试
if ($currentSize > $maxSizeBytes) {
$currentQuality -= 5;
}
}
// 替换原始文件
if ($currentSize < $originalSize) {
rename($tempPath, $filePath);
$this->info("优化成功: $imagePath{$this->formatBytes($originalSize)} 压缩到 {$this->formatBytes($currentSize)}");
} else {
@unlink($tempPath);
$this->warn("压缩失败: $imagePath 无法达到目标大小");
}
$progressBar->advance();
$count++;
}
$progressBar->finish();
$this->info("\n{$count}/{$total} 个图片已优化完成");
}
/**
* 重新压缩图片
*/
protected function recompressImage($filePath, $quality)
{
// 读取原始图片
$image = imagecreatefromwebp($filePath);
// 捕捉错误
if (!$image) {
throw new \Exception("无法读取图片: $filePath");
}
// 捕捉输出
ob_start();
$success = imagewebp($image, null, $quality);
if (!$success) {
throw new \Exception("压缩失败: $filePath");
}
$compressedContent = ob_get_clean();
imagedestroy($image);
return $compressedContent;
}
/**
* 格式化字节大小
*/
protected function formatBytes($bytes)
{
if ($bytes < 1024) return $bytes . 'B';
if ($bytes < 1048576) return round($bytes / 1024, 1) . 'KB';
return round($bytes / 1048576, 1) . 'MB';
}
}