图片水印支持

This commit is contained in:
Wisp X
2022-01-18 22:57:34 +08:00
parent 526e32a5f0
commit 6a0704b183
3 changed files with 92 additions and 44 deletions
+22
View File
@@ -4,5 +4,27 @@ namespace App\Enums\Watermark;
final class ImageOption
{
/** @var string 水印图片 */
const Image = 'image';
/** @var string 水印位置 */
const Position = 'position';
/** @var string 水印图片宽 */
const Width = 'width';
/** @var string 水印图片高 */
const Height = 'height';
/** @var string 旋转角度 */
const Rotate = 'rotate';
/** @var string 不透明度 */
const Opacity = 'opacity';
/** @var string X轴偏移量 */
const X = 'x';
/** @var string Y轴偏移量 */
const Y = 'y';
}
+60 -42
View File
@@ -11,6 +11,7 @@ use App\Enums\StrategyKey;
use App\Enums\UserConfigKey;
use App\Enums\UserStatus;
use App\Enums\Watermark\FontOption;
use App\Enums\Watermark\ImageOption;
use App\Exceptions\UploadException;
use App\Models\Group;
use App\Models\Image;
@@ -45,7 +46,7 @@ class ImageService
{
$file = $request->file('file');
if (is_null($user) && ! Utils::config(ConfigKey::IsAllowGuestUpload, true)) {
if (is_null($user) && !Utils::config(ConfigKey::IsAllowGuestUpload, true)) {
throw new UploadException('管理员关闭了游客上传');
}
@@ -60,8 +61,8 @@ class ImageService
'configs' => collect([LocalOption::Root => config('filesystems.disks.uploads.root')]),
]);
if (! is_null($user)) {
if (Utils::config(ConfigKey::IsUserNeedVerify) && ! $user->email_verified_at) {
if (!is_null($user)) {
if (Utils::config(ConfigKey::IsUserNeedVerify) && !$user->email_verified_at) {
throw new UploadException('账户未验证');
}
@@ -99,7 +100,7 @@ class ImageService
}
}
if (! in_array($file->extension(), $configs->get(GroupConfigKey::AcceptedFileSuffixes))) {
if (!in_array($file->extension(), $configs->get(GroupConfigKey::AcceptedFileSuffixes))) {
throw new UploadException('不支持的文件类型');
}
@@ -119,7 +120,7 @@ class ImageService
$sql = collect(array_keys($array))->transform(function ($range) use ($carbon, $format) {
return "count(if(`created_at` between '{$carbon->parse("-1 {$range}")->format($format)}' and '{$carbon->format($format)}', 1, null)) as {$range}";
})->implode(', ');
$statistics = Image::query()->selectRaw($sql)->when(! is_null($user), function (Builder $builder) use ($user) {
$statistics = Image::query()->selectRaw($sql)->when(!is_null($user), function (Builder $builder) use ($user) {
$builder->where('user_id', $user->id);
}, function (Builder $builder) use ($request) {
$builder->where('uploaded_ip', $request->ip())->whereNull('user_id');
@@ -171,12 +172,14 @@ class ImageService
}
DB::transaction(function () use ($image, $user, $filesystem, $existing) {
if (! $image->save()) {
if (!$image->save()) {
// 删除文件
if (is_null($existing)) $filesystem->delete($image->pathname);
if (is_null($existing)) {
$filesystem->delete($image->pathname);
}
throw new UploadException('图片保存失败');
}
if (! is_null($user)) {
if (!is_null($user)) {
$user->increment('image_num');
}
}, 3);
@@ -200,8 +203,8 @@ class ImageService
/**
* 合成验证码
*
* @param mixed $image
* @param Collection $configs
* @param mixed $image
* @param Collection $configs
* @return \Intervention\Image\Image
*/
public function stickWatermark(mixed $image, Collection $configs): \Intervention\Image\Image
@@ -211,50 +214,65 @@ class ImageService
$image = InterventionImage::make($image);
$position = $options->get(FontOption::Position, 'top-right');
$offsetX = (int) $options->get(FontOption::X, 10);
$offsetY = (int) $options->get(FontOption::Y, 10);
if ($driver === 'font') {
$text = $options->get(FontOption::Text, Utils::config(ConfigKey::SiteName));
$font = new Font(urldecode($text));
$font->valign('top')
->file($options->get(FontOption::Font))
->size($options->get(FontOption::Size, 50))
->angle($options->get(FontOption::Angle, 0))
->file(storage_path('app/public/test.ttf'))
->size((int) $options->get(FontOption::Size, 50))
->angle((int) $options->get(FontOption::Angle, 0))
->color($options->get(FontOption::Color, '000000')); // 十六进制 or rgba
$box = $font->getBoxSize();
$font->text($text);
$manager = new ImageManager();
$canvas = $manager->canvas($box['width'], $box['height']);
// 原图宽高
$imageWidth = $image->width();
$imageHeight = $image->height();
// 水印画布宽高
$canvasWidth = $canvas->width();
$canvasHeight = $canvas->height();
$offsetX = $options->get(FontOption::X, 0);
$offsetY = $options->get(FontOption::Y, 0);
if ($position === 'tiled') {
// 平铺水印
for ($x = 0; $x < $imageWidth; $x++) {
for ($y = 0; $y < $imageHeight; $y++) {
$image->text($text, $x, $y, fn (Font &$f) => $f = $font);
$y += $canvasHeight + $offsetY;
}
$x += $canvasWidth + $offsetX;
}
} else {
$font->applyToImage($canvas);
$image->insert($manager->make($canvas), $position, $offsetX, $offsetY);
}
$font->applyToImage($canvas);
$watermark = $manager->make($canvas);
}
if ($driver === 'image') {
// TODO
$watermark = InterventionImage::make($options->get(ImageOption::Image));
$opacity = (int) $options->get(ImageOption::Opacity, 0);
$width = $options->get(ImageOption::Width, 0);
$height = $options->get(ImageOption::Height, 0);
if ($opacity && $opacity != 100) {
$watermark->opacity((int) min($opacity, 100));
}
if ($width + $height > 0) {
$watermark->resize($width ?: null, $height ?: null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
});
}
}
// 原图宽高
$imageWidth = $image->width();
$imageHeight = $image->height();
// 水印画布宽高
$watermarkWidth = $watermark->width();
$watermarkHeight = $watermark->height();
if ($position === 'tiled') {
// 平铺水印
for ($x = 0; $x < $imageWidth; $x++) {
for ($y = 0; $y < $imageHeight; $y++) {
$image->insert($watermark, '', $x, $y);
$y += $watermarkHeight + $offsetY;
}
$x += $watermarkWidth + $offsetX;
}
} else {
$image->insert($watermark, $position, $offsetX, $offsetY);
}
// TODO 可读性差,需要改进
return $image;
}
@@ -265,10 +283,10 @@ class ImageService
'{y}' => date('y'),
'{m}' => date('m'),
'{d}' => date('d'),
'timestamp' => time(),
'{timestamp}' => time(),
'{uniqid}' => uniqid(),
'{md5}' => md5(microtime() . Str::random()),
'{md5-16}' => substr(md5(microtime() . Str::random()), 0, 16),
'{md5}' => md5(microtime().Str::random()),
'{md5-16}' => substr(md5(microtime().Str::random()), 0, 16),
'{str-random-16}' => Str::random(),
'{str-random-10}' => Str::random(10),
'{filename}' => rtrim($file->getClientOriginalName(), '.'.$file->extension()),
+10 -2
View File
@@ -6,6 +6,7 @@ use App\Enums\ConfigKey;
use App\Enums\GroupConfigKey;
use App\Enums\Mail\SmtpOption;
use App\Enums\Watermark\FontOption;
use App\Enums\Watermark\ImageOption;
use App\Models\Group;
use Illuminate\Database\Seeder;
use Illuminate\Support\Carbon;
@@ -54,7 +55,7 @@ class DatabaseSeeder extends Seeder
'drivers' => [
'font' => [
FontOption::Text => 'Lsky Pro',
FontOption::Position => 'top-right',
FontOption::Position => 'bottom-right',
FontOption::Angle => 0,
FontOption::Size => 50,
FontOption::Font => '',
@@ -63,7 +64,14 @@ class DatabaseSeeder extends Seeder
FontOption::Y => 10,
],
'image' => [
// TODO
ImageOption::Image => '',
ImageOption::Position => 'bottom-right',
ImageOption::Opacity => 100,
ImageOption::Rotate => 0,
ImageOption::Width => 0,
ImageOption::Height => 0,
ImageOption::X => 10,
ImageOption::Y => 10,
]
],
],