Laravel 13 图片处理完全指南
图片处理是现代 Web 应用的重要功能。本文将深入探讨 Laravel 13 中图片处理的各种技巧和最佳实践。
安装配置
安装 Intervention Image
1
| composer require intervention/image
|
配置
1 2 3 4
| return [ 'driver' => env('IMAGE_DRIVER', 'gd'), ];
|
基础图片操作
创建图片实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| use Intervention\Image\Facades\Image;
$img = Image::make('public/image.jpg');
$img = Image::make($request->file('image'));
$img = Image::make(file_get_contents('image.jpg'));
$img = Image::make('data:image/png;base64,...');
$img = Image::canvas(800, 600, '#ffffff');
|
调整大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| $img->resize(300, 200);
$img->resize(300, null);
$img->resize(null, 200);
$img->resize(300, null, function ($constraint) { $constraint->aspectRatio(); });
$img->resize(300, null, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); });
|
裁剪
1 2 3 4 5 6 7 8 9 10 11 12 13
| $img->crop(200, 200);
$img->crop(200, 200, 100, 50);
$img->fit(300, 200);
$img->fit(300, 200, function ($constraint) { $constraint->upsize(); });
|
图片滤镜
调整亮度对比度
1 2 3 4 5 6 7 8
| $img->brightness(30);
$img->contrast(50);
$img->gamma(1.6);
|
颜色调整
1 2 3 4 5 6 7 8 9 10 11
| $img->greyscale();
$img->invert();
$img->colorize(100, 0, 0);
$img->limitColors(255, '#ffffff');
|
模糊与锐化
1 2 3 4 5 6 7 8 9
| $img->blur(); $img->blur(15);
$img->sharpen(15);
$img->pixelate(12);
|
滤镜效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| $img->filter(new Intervention\Image\Filters\DemoFilter);
class VintageFilter implements Intervention\Image\Filters\FilterInterface { public function applyFilter(Intervention\Image\Image $image) { return $image ->brightness(-10) ->contrast(10) ->colorize(20, 0, -20); } }
$img->filter(new VintageFilter);
|
添加水印
文字水印
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| $img->text('Watermark', 10, 10, function ($font) { $font->size(24); $font->color('#ffffff'); $font->align('left'); $font->valign('top'); $font->angle(45); $font->file(public_path('fonts/arial.ttf')); });
$img->text('Center', $img->width() / 2, $img->height() / 2, function ($font) { $font->size(40); $font->color('#ffffff'); $font->align('center'); $font->valign('middle'); });
|
图片水印
1 2 3 4 5 6 7 8 9 10 11
| $watermark = Image::make('watermark.png');
$img->insert($watermark, 'bottom-right', 10, 10);
$img->insert($watermark, 'center');
$img->insert($watermark, 'top-left', 0, 0);
$watermark->opacity(50); $img->insert($watermark, 'bottom-right', 10, 10);
|
图片绘制
绘制形状
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| $img->line(10, 10, 100, 100, function ($draw) { $draw->color('#ff0000'); $draw->width(3); });
$img->rectangle(10, 10, 100, 100, function ($draw) { $draw->background('#0000ff'); $draw->border(2, '#ff0000'); });
$img->circle(50, 50, 50, function ($draw) { $draw->background('#00ff00'); $draw->border(2, '#000000'); });
$img->ellipse(80, 40, 50, 50, function ($draw) { $draw->background('#ffff00'); });
|
填充
1 2 3 4 5 6 7 8
| $img->fill('#ff0000');
$img->fill('linear:(#ff0000)-(#0000ff)');
$img->fill(Image::make('pattern.png'));
|
图片编码
输出格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| $img->encode('jpg', 80);
$img->encode('png');
$img->encode('webp', 90);
$img->encode('gif');
$base64 = (string) $img->encode('data-url');
|
保存图片
1 2 3 4 5 6 7 8 9
| $img->save('path/to/image.jpg');
$img->save('path/to/image.jpg', 80);
$img->save('path/to/image.png'); $img->save('path/to/image.webp');
|
图片上传服务
完整上传服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| <?php
namespace App\Services;
use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Storage; use Intervention\Image\Facades\Image;
class ImageService { protected array $sizes = [ 'thumbnail' => [150, 150], 'small' => [300, 300], 'medium' => [600, 600], 'large' => [1200, 1200], ];
protected array $watermarks = [ 'large' => 'watermarks/large.png', 'medium' => 'watermarks/medium.png', ];
public function upload(UploadedFile $file, string $directory = 'images'): array { $hash = md5_file($file->path()); $extension = 'webp'; $filename = "{$hash}.{$extension}";
$paths = [];
$original = Image::make($file); $originalPath = "{$directory}/original/{$filename}"; Storage::put($originalPath, $original->encode('webp', 100)); $paths['original'] = $originalPath;
foreach ($this->sizes as $name => [$width, $height]) { $img = clone $original; $img->fit($width, $height, function ($constraint) { $constraint->upsize(); });
if (isset($this->watermarks[$name])) { $this->addWatermark($img, $this->watermarks[$name]); }
$path = "{$directory}/{$name}/{$filename}"; Storage::put($path, $img->encode('webp', 85)); $paths[$name] = $path; }
return [ 'paths' => $paths, 'filename' => $filename, 'width' => $original->width(), 'height' => $original->height(), ]; }
protected function addWatermark($img, string $watermarkPath): void { if (Storage::exists($watermarkPath)) { $watermark = Image::make(Storage::path($watermarkPath)); $watermark->opacity(30); $img->insert($watermark, 'bottom-right', 10, 10); } }
public function delete(string $directory, string $filename): void { Storage::delete("{$directory}/original/{$filename}"); foreach (array_keys($this->sizes) as $name) { Storage::delete("{$directory}/{$name}/{$filename}"); } } }
|
图片优化
压缩优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| <?php
namespace App\Services;
use Intervention\Image\Facades\Image;
class ImageOptimizer { public function optimize(string $path, array $options = []): void { $img = Image::make($path);
$img->orientate();
$maxWidth = $options['max_width'] ?? 1920; $maxHeight = $options['max_height'] ?? 1080;
if ($img->width() > $maxWidth || $img->height() > $maxHeight) { $img->resize($maxWidth, $maxHeight, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); }); }
if ($options['limit_colors'] ?? true) { $img->limitColors(255); }
$quality = $options['quality'] ?? 85; $format = $options['format'] ?? pathinfo($path, PATHINFO_EXTENSION);
$img->save($path, $quality, $format); }
public function batchOptimize(string $directory, array $options = []): array { $results = []; $files = Storage::allFiles($directory);
foreach ($files as $file) { if ($this->isImage($file)) { $path = Storage::path($file); $originalSize = filesize($path);
$this->optimize($path, $options);
$newSize = filesize($path); $saved = $originalSize - $newSize;
$results[] = [ 'file' => $file, 'original_size' => $originalSize, 'new_size' => $newSize, 'saved' => $saved, 'saved_percent' => round(($saved / $originalSize) * 100, 2), ]; } }
return $results; }
protected function isImage(string $file): bool { $extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp']; return in_array(strtolower(pathinfo($file, PATHINFO_EXTENSION)), $extensions); } }
|
图片验证
自定义验证规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| <?php
namespace App\Rules;
use Closure; use Illuminate\Contracts\Validation\ValidationRule; use Intervention\Image\Facades\Image;
class ValidImage implements ValidationRule { protected int $minWidth; protected int $minHeight; protected int $maxWidth; protected int $maxHeight;
public function __construct( int $minWidth = 0, int $minHeight = 0, int $maxWidth = 10000, int $maxHeight = 10000 ) { $this->minWidth = $minWidth; $this->minHeight = $minHeight; $this->maxWidth = $maxWidth; $this->maxHeight = $maxHeight; }
public function validate(string $attribute, mixed $value, Closure $fail): void { try { $img = Image::make($value);
if ($img->width() < $this->minWidth) { $fail("图片宽度至少需要 {$this->minWidth} 像素"); }
if ($img->height() < $this->minHeight) { $fail("图片高度至少需要 {$this->minHeight} 像素"); }
if ($img->width() > $this->maxWidth) { $fail("图片宽度不能超过 {$this->maxWidth} 像素"); }
if ($img->height() > $this->maxHeight) { $fail("图片高度不能超过 {$this->maxHeight} 像素"); } } catch (\Exception $e) { $fail('无效的图片文件'); } } }
|
图片处理队列
异步处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| <?php
namespace App\Jobs;
use App\Models\Image; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Support\Facades\Storage; use Intervention\Image\Facades\Image;
class ProcessImage implements ShouldQueue { use Queueable;
protected array $sizes = [ 'thumbnail' => [150, 150], 'medium' => [600, 600], 'large' => [1200, 1200], ];
public function __construct( protected Image $imageModel ) {}
public function handle(): void { $originalPath = $this->imageModel->original_path; $directory = dirname($originalPath); $filename = basename($originalPath);
$img = Image::make(Storage::path($originalPath));
$paths = [];
foreach ($this->sizes as $name => [$width, $height]) { $resized = clone $img; $resized->fit($width, $height, function ($constraint) { $constraint->upsize(); });
$path = "{$directory}/{$name}/{$filename}"; Storage::put($path, $resized->encode('webp', 85)); $paths[$name] = $path; }
$this->imageModel->update([ 'paths' => $paths, 'width' => $img->width(), 'height' => $img->height(), 'processed_at' => now(), ]); } }
|
总结
Laravel 13 的图片处理提供了:
- 强大的图片编辑功能
- 灵活的尺寸调整
- 丰富的滤镜效果
- 水印添加支持
- 多格式编码输出
- 批量处理优化
- 异步队列处理
掌握图片处理技巧可以构建功能丰富的媒体应用。