From ffa0248daed306cd000b6268ca3ba5e4353e0ce0 Mon Sep 17 00:00:00 2001 From: lq Date: Tue, 17 Jun 2025 10:19:06 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8E=8B=E7=BC=A9=E5=9B=BE=E7=89=87=E5=91=BD?= =?UTF-8?q?=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Commands/OptimizeImageCompression.php | 150 +++ resources/views/compress.blade.php | 872 ++++++++++++++++++ routes/web.php | 4 +- 3 files changed, 1025 insertions(+), 1 deletion(-) create mode 100644 app/Console/Commands/OptimizeImageCompression.php create mode 100644 resources/views/compress.blade.php diff --git a/app/Console/Commands/OptimizeImageCompression.php b/app/Console/Commands/OptimizeImageCompression.php new file mode 100644 index 00000000..3d8d0412 --- /dev/null +++ b/app/Console/Commands/OptimizeImageCompression.php @@ -0,0 +1,150 @@ +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'; + } +} diff --git a/resources/views/compress.blade.php b/resources/views/compress.blade.php new file mode 100644 index 00000000..70f94008 --- /dev/null +++ b/resources/views/compress.blade.php @@ -0,0 +1,872 @@ + + + + + + 图片压缩工具 + + + + +
+
+ +
+

批量压缩并替换图片文件

+
+
+ +
+ + +
+
+
+

原始图片

+
+
+
+
+ +

请从左侧选择一张图片

+
+
+
+
+
文件大小
+
0 KB
+
+
+
分辨率
+
0×0
+
+
+
+
+ +
+
+

压缩后图片

+
+
+
+
+ +

压缩后图片将显示在这里

+
+
+
+
+
文件大小
+
0 KB
+
+
+
分辨率
+
0×0
+
+
+
+ 尺寸减少:0% +
+
+
+ + +
+
+

批量压缩进度

+
+
+
+ +

批量压缩状态将显示在这里

+
+ + + +
+ +

压缩完成!所有文件已更新

+
+
+
+
+
+
+ + +
+
+

确认批量操作

+

+ 您将压缩 0 个文件,覆盖其原始文件。
+ 此操作不可逆,请确保您已备份重要文件。 +

+
+ + +
+
+
+ + + + diff --git a/routes/web.php b/routes/web.php index ea41dffd..5407da37 100644 --- a/routes/web.php +++ b/routes/web.php @@ -18,5 +18,7 @@ Route::get('/', function () { 'photo_album' => $photoAlbum, ]); }); - +Route::get('/images/compress', function() { + return view('compress'); +}); Route::post('/upload', [\App\Http\Controllers\ImageController::class, 'upload']);