путь к файлу, 'size' => размер в байтах, 'original_size' => исходный размер] */ public function run(UploadedFile $file): array { $extension = strtolower($file->getClientOriginalExtension()); Log::channel('app')->info('Начало обработки изображения', [ 'file' => $file->getClientOriginalName(), 'extension' => $extension, 'size' => $this->formatFileSize($file->getSize()), 'size_bytes' => $file->getSize(), ]); // Если файл уже меньше порога и это WebP - пропускаем сжатие if ($file->getSize() <= self::SIZE_THRESHOLD && $extension === 'webp') { Log::channel('app')->info('Файл уже оптимизирован, пропускаем', [ 'file' => $file->getClientOriginalName(), ]); return [ 'file' => $file, 'size' => $file->getSize(), 'original_size' => $file->getSize(), 'compressed' => false, ]; } // Создаём изображение через Intervention $img = Image::make($file->getRealPath()); // Получаем исходные размеры $originalWidth = $img->width(); $originalHeight = $img->height(); Log::channel('app')->info('Исходные размеры', [ 'width' => $originalWidth, 'height' => $originalHeight, ]); // Ресайзим если больше максимальных размеров if ($originalWidth > self::MAX_WIDTH || $originalHeight > self::MAX_HEIGHT) { $img->resize(self::MAX_WIDTH, self::MAX_HEIGHT, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); }); Log::channel('app')->info('Изображение ресайзнуто', [ 'new_width' => $img->width(), 'new_height' => $img->height(), ]); } // Конвертируем в WebP и сжимаем $webpData = $img->encode('webp', self::WEBP_QUALITY)->getEncoded(); // Сохраняем во временный файл $tempFileName = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME) . '.webp'; $tempPath = sys_get_temp_dir() . '/' . uniqid('img_compress_') . '.webp'; file_put_contents($tempPath, $webpData); $newSize = filesize($tempPath); Log::channel('app')->info('Сжатие завершено', [ 'original_size' => $this->formatFileSize($file->getSize()), 'compressed_size' => $this->formatFileSize($newSize), 'compression_ratio' => round((1 - $newSize / $file->getSize()) * 100, 2) . '%', 'temp_path' => $tempPath, 'memory_usage' => round(memory_get_usage(true) / 1024 / 1024, 2) . ' MB', ]); // Создаём новый UploadedFile из сжатого $compressedFile = new UploadedFile( $tempPath, $tempFileName, 'image/webp', $newSize, true ); return [ 'file' => $compressedFile, 'size' => $newSize, 'original_size' => $file->getSize(), 'compressed' => true, 'compression_ratio' => round((1 - $newSize / $file->getSize()) * 100, 2), ]; } /** * Проверяет, является ли файл изображением */ public function isImage(UploadedFile $file): bool { $extension = strtolower($file->getClientOriginalExtension()); $imageExtensions = ['jpg', 'jpeg', 'png', 'webp', 'gif', 'bmp', 'tiff', 'svg']; return in_array($extension, $imageExtensions); } /** * Форматирует размер файла */ private function formatFileSize(int $bytes): string { $units = ['б', 'КиБ', 'МиБ', 'ГиБ']; $bytes = max($bytes, 0); $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); $bytes /= (1 << (10 * $pow)); return round($bytes, 2) . ' ' . $units[$pow]; } }