perf: 优化缩略图

This commit is contained in:
kuaifan
2024-10-29 21:06:08 +08:00
parent 50a7950ccd
commit d799c06017
7 changed files with 257 additions and 33 deletions

View File

@@ -3,9 +3,11 @@
namespace App\Exceptions;
use App\Module\Base;
use App\Module\Image;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Throwable;
class Handler extends ExceptionHandler
@@ -51,6 +53,11 @@ class Handler extends ExceptionHandler
*/
public function render($request, Throwable $e)
{
if ($e instanceof NotFoundHttpException) {
if ($result = $this->ImagePathHandler($request)) {
return $result;
}
}
if ($e instanceof ApiException) {
return response()->json(Base::retError($e->getMessage(), $e->getData(), $e->getCode()));
} elseif ($e instanceof ModelNotFoundException) {
@@ -78,4 +85,133 @@ class Handler extends ExceptionHandler
parent::report($e);
}
}
/**
* 图片路径处理
* @param $request
* @return \Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\BinaryFileResponse|null
*/
private function ImagePathHandler($request)
{
$path = $request->path();
// 处理图片
$pattern = '/^(uploads\/.*\.(png|jpg|jpeg))\/crop\/([^\/]+)$/';
if (preg_match($pattern, $path, $matches)) {
// 获取参数
$file = $matches[1];
$ext = $matches[2];
$rules = preg_replace('/\s+/', '', $matches[3]);
$rules = str_replace(['=', '&'], [':', ','], $rules);
$rules = explode(',', $rules);
if (empty($rules)) {
return null;
}
// 提取年月
$Ym = date("Ym");
if (preg_match('/\/(\d{6})\//', $file, $ms)) {
$Ym = $ms[1];
}
// 文件存在直接返回
$dirName = str_replace(['/', '.'], '_', $file);
$fileName = str_replace([':', ','], ['-', '_'], implode(',', $rules)) . '.' . $ext;
$savePath = public_path('uploads/tmp/crop/' . $Ym . '/' . $dirName . '/' . $fileName);
if (file_exists($savePath)) {
// 设置头部声明图片缓存
return response()->file($savePath, [
'Pragma' => 'public',
'Cache-Control' => 'max-age=1814400',
'Expires' => gmdate('D, d M Y H:i:s', time() + 1814400) . ' GMT',
'Last-Modified' => gmdate('D, d M Y H:i:s', filemtime($savePath)) . ' GMT',
'ETag' => md5_file($savePath)
]);
}
// 文件不存在处理
$sourcePath = public_path($file);
if (!file_exists($sourcePath)) {
return null;
}
// 判断删除多余文件
$saveDir = dirname($savePath);
if (is_dir($saveDir)) {
$items = glob($saveDir . '/*');
if (count($items) > 5) {
usort($items, function ($a, $b) {
return filemtime($b) - filemtime($a);
});
$itemsToDelete = array_slice($items, 5);
foreach ($itemsToDelete as $item) {
if (is_file($item)) {
unlink($item);
}
}
}
} else {
Base::makeDir($saveDir);
}
// 处理图片
try {
$handle = 0;
$image = new Image($sourcePath);
foreach ($rules as $rule) {
if (!str_contains($rule, ':')) {
continue;
}
[$type, $value] = explode(':', $rule);
if (!in_array($type, ['ratio', 'size', 'percentage', 'cover', 'contain'])) {
continue;
}
switch ($type) {
// 按比例裁剪
case 'ratio':
if (is_numeric($value)) {
$image->ratioCrop($value);
$handle++;
}
break;
// 按尺寸缩放
case 'size':
$size = Base::newIntval(explode('x', $value));
if (count($size) === 2) {
$image->resize($size[0], $size[1]);
$handle++;
}
break;
// 按尺寸缩放
case 'percentage':
case 'cover':
case 'contain':
$size = Base::newIntval(explode('x', $value));
if (count($size) === 2) {
$image->thumb($size[0], $size[1], $type);
$handle++;
}
break;
}
}
if ($handle > 0) {
$image->saveTo($savePath);
Image::compressImage($savePath, null, 80);
return response()->file($savePath, [
'Pragma' => 'public',
'Cache-Control' => 'max-age=1814400',
'Expires' => gmdate('D, d M Y H:i:s', time() + 1814400) . ' GMT',
'Last-Modified' => gmdate('D, d M Y H:i:s', filemtime($savePath)) . ' GMT',
'ETag' => md5_file($savePath)
]);
} else {
$image->destroy();
}
} catch (\ImagickException) { }
}
return null;
}
}