feat: 图片上传水印处理

This commit is contained in:
2025-06-11 14:46:40 +08:00
parent 613727fe44
commit 6b0bd13fb7
2 changed files with 179 additions and 2 deletions

View File

@@ -141,6 +141,44 @@ class SiteConfig
return $ret;
}
// 根据分组获取配置
public function getByGroupUniqueLabel($unique_label)
{
$configs = SysConfigModel::alias('c')
->field([
'c.id',
'c.title',
'c.name',
'c.value',
])
->join(SysConfigGroupModel::getTable(). ' g', 'g.id = c.group_id')
->where('g.language_id', '=', request()->lang_id)
->where('g.unique_label', '=', $unique_label)
->where('g.status', '=', 1)
->order(['c.sort' => 'asc', 'c.id' => 'desc'])
->select()
->each(function ($item) {
// 修改字段为null的输出为空字符串
$keys = array_keys($item->toArray());
foreach ($keys as $key) {
if (is_null($item[$key])) {
$item[$key] = '';
}
}
return $item;
})
->toArray();
if (!empty($configs)) {
$configs_map = [];
foreach ($configs as $cfg) {
$configs_map[$cfg['name']] = $cfg;
}
return $configs_map;
}
return [];
}
// 更新配置
public function update()
{

View File

@@ -7,6 +7,7 @@ use app\admin\model\v1\SysImageUploadRecordModel;
use app\admin\model\v1\SysVideoUploadRecordModel;
use app\admin\model\v1\SysAttachmentUploadRecordModel;
use Intervention\Image\ImageManager;
use Intervention\Image\Typography\FontFactory;
use think\facade\Filesystem;
/**
@@ -44,9 +45,68 @@ class Upload
$image_model = SysImageUploadRecordModel::md5($filemd5)->find();
if (is_null($image_model)) {
$filename = Filesystem::disk('image')->putFile($param['module'], $file);
// 生成缩略图
$image_manager = new ImageManager(new \Intervention\Image\Drivers\Imagick\Driver());
// 处理图片
$image_manager = ImageManager::imagick();
$image = $image_manager->read('.' . $storage . '/' . $filename);
// 水印
list($enabled, $type, $text_options, $image_options) = $this->getWatermarkOptions();
if ($enabled) {
// 图片水印
if ($type == 'IMAGE' && $image_options['image'] != '') {
// 读取水印图片
$watermark_image = $image_manager->read($image_options['image']);
// 缩放水印图片
$watermark_image->scale($image_options['width'], $image_options['height']);
// 绘制水印图片
$image->place(
$watermark_image,
$image_options['position'],
$image_options['offset_x'],
$image_options['offset_y'],
$image_options['opacity']
);
}
// 文字水印
else if ($type == 'TEXT' && $text_options['txt'] != '') {
// 原图宽度
$origin_width = $image->width();
// 原图高度
$origin_height = $image->height();
$font_factory = new FontFactory(function(FontFactory $font) use($text_options) {
// 设置字体
$font->filename($text_options['font']);
// 设置字体大小
$font->size($text_options['size']);
// 设置字体颜色及透明度
$opacity = dechex((int)ceil(255 * ($text_options['opacity'] / 100)));
$font->color($text_options['color'] . $opacity);
});
// 文字尺寸
$font_rect = $image->driver()->fontProcessor()->boxSize($text_options['txt'], $font_factory());
// 计算偏移量
list($offset_x, $offset_y) = $this->scaleTxtOffsetXYByPosition(
$text_options['position'],
$text_options['offset_x'],
$text_options['offset_y'],
$origin_width,
$origin_height,
$font_rect->width(),
$font_rect->height()
);
// 绘制文字
$image->text(
$text_options['txt'],
$offset_x,
$offset_y,
$font_factory()
);
}
$image->save('.'. $storage. '/'. $filename);
}
// 缩略图
$image->scale(200, 200);
$idx = strrpos($filename, '.');
$thumb_filename = mb_substr($filename, 0, $idx) . '_thumb.' . mb_substr($filename, $idx + 1);
@@ -79,6 +139,85 @@ class Upload
return error('上传失败');
}
/**
* 获取水印配置
*
* @return array
*/
private function getWatermarkOptions(): array
{
$config_model = new \app\admin\controller\v1\SiteConfig;
$watermark_config = $config_model->getByGroupUniqueLabel('watermark');
return [
'enalbed' => data_get($watermark_config, 'watermark_enabled.value', 0) == 1,
'type' => data_get($watermark_config, 'watermark_type.value', ''),
'text_options' => [
'txt' => data_get($watermark_config, 'watermark_text_value.value', ''),
'font' => data_get($watermark_config, 'watermark_font_family.value', ''),
'size' => (float)data_get($watermark_config, 'watermark_text_size.value', 12),
'color' => data_get($watermark_config, 'watermark_text_color.value', '#000000'),
'position' => data_get($watermark_config, 'watermark_position.value', 'top-left'),
'offset_x' => (int)data_get($watermark_config, 'watermark_offset_x.value', 0),
'offset_y' => (int)data_get($watermark_config, 'watermark_offset_y.value', 0),
'opacity' => (int)data_get($watermark_config, 'watermark_opacity.value', 100),
],
'image_options' => [
'image' => data_get($watermark_config, 'watermark_image_value.value', ''),
'width' => data_get($watermark_config, 'watermark_image_width.value', null),
'height' => data_get($watermark_config, 'watermark_image_height.value', null),
'position' => data_get($watermark_config, 'watermark_position.value', 'top-left'),
'offset_x' => (int)data_get($watermark_config, 'watermark_offset_x.value', 0),
'offset_y' => (int)data_get($watermark_config, 'watermark_offset_y.value', 0),
'opacity' => (int)data_get($watermark_config, 'watermark_opacity.value', 100),
],
];
}
/**
* 计算文体水印偏移量
*
* @param string $position
* @param integer $offset_x
* @param integer $offset_y
* @param integer $image_width
* @param integer $image_height
* @param integer $txt_width
* @param integer $txt_height
* @return array
*/
private function scaleTxtOffsetXYByPosition(string $position, int $offset_x, int $offset_y, int $image_width, int $image_height, int $txt_width, int $txt_height)
{
switch ($position) {
case 'top-left':
// top-left:左上角
return [$offset_x, $offset_y];
case 'top-right':
// top-right:右上角
return [(int)($image_width-$txt_width-$offset_x), $offset_y];
case 'top':
// top:上 - 水平居中
return [(int)(($image_width-$txt_width+$offset_x)/2), $offset_y];
case 'left':
// left:左 - 垂直居中
return [$offset_x, (int)(($image_height-$txt_height)/2+$offset_y)];
case 'center':
// center:水平垂直居中
return [(int)(($image_width-$txt_width)/2+$offset_x), (int)(($image_height-$txt_height)/2+$offset_y)];
case 'right':
// right:右 - 垂直居中
return [(int)($image_width-$txt_width-$offset_x), (int)(($image_height-$txt_height)/2+$offset_y)];
case'bottom':
// bottom:下 - 水平居中
return [(int)(($image_width-$txt_width+$offset_x)/2), (int)($image_height-$txt_height-$offset_y)];
case'bottom-left':
// bottom-left:左下角
return [$offset_x, (int)($image_height-$txt_height-$offset_y)];
case'bottom-right':
// bottom-right:右下角
return [(int)($image_width-$txt_width-$offset_x), (int)($image_height-$txt_height-$offset_y)];
default:
throw new \InvalidArgumentException('Invalid position');
}
}
/**
* 上传视频