Procházet zdrojové kódy

feat:增加支持水印平铺

ChenGanBin před 2 roky
rodič
revize
ff8a3e7c9b

+ 5 - 1
pom.xml

@@ -51,7 +51,11 @@
             <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
             <version>2.1.0</version>
         </dependency>
-
+        <dependency>
+            <groupId>net.coobird</groupId>
+            <artifactId>thumbnailator</artifactId>
+            <version>0.4.20</version>
+        </dependency>
     </dependencies>
 
     <!-- 配置阿里云仓库 -->

+ 4 - 1
src/main/java/com/zhixinghe1/ots/common/aop/WebOperateLogAspect.java

@@ -9,6 +9,9 @@ import org.aspectj.lang.annotation.Pointcut;
 import org.springframework.stereotype.Component;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.Arrays;
 
 @Slf4j
 @Aspect
@@ -27,7 +30,7 @@ public class WebOperateLogAspect {
         HttpServletRequest request = requestAttributes.getRequest();
 
         String requestURI = request.getRequestURI();
-        Object[] args = joinPoint.getArgs();
+        Object[] args = Arrays.stream(joinPoint.getArgs()).filter(arg -> !(arg instanceof MultipartFile)).toArray();
         Object result = null;
         Exception exception = null;
         try {

+ 17 - 0
src/main/java/com/zhixinghe1/ots/common/converter/IJsonEnum.java

@@ -0,0 +1,17 @@
+package com.zhixinghe1.ots.common.converter;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Json返回值枚举接口
+ *
+ * @author tyuio
+ */
+public interface IJsonEnum<T> {
+
+    /**
+     * 获取枚举编码
+     */
+    @JsonValue
+    T getCode();
+}

+ 22 - 0
src/main/java/com/zhixinghe1/ots/common/converter/IntegerCodeToEnumConverterFactory.java

@@ -0,0 +1,22 @@
+package com.zhixinghe1.ots.common.converter;
+
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.core.convert.converter.ConverterFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class IntegerCodeToEnumConverterFactory implements ConverterFactory<Integer, IJsonEnum> {
+
+    private static final Map<Class, Converter> CONVERTERS = new HashMap<>();
+
+    @Override
+    public <T extends IJsonEnum> Converter<Integer, T> getConverter(Class<T> targetType) {
+        Converter<Integer, T> converter = CONVERTERS.get(targetType);
+        if (converter == null) {
+            converter = new IntegerToEnumConverter<>(targetType);
+            CONVERTERS.put(targetType, converter);
+        }
+        return converter;
+    }
+}

+ 33 - 0
src/main/java/com/zhixinghe1/ots/common/converter/IntegerToEnumConverter.java

@@ -0,0 +1,33 @@
+package com.zhixinghe1.ots.common.converter;
+
+import org.springframework.core.convert.converter.Converter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 转换器 数值转换为枚举
+ *
+ * @author tyuio
+ */
+public class IntegerToEnumConverter<T extends IJsonEnum<Integer>> implements Converter<Integer, T> {
+
+    private final Map<Integer, T> enumMap = new HashMap<>();
+
+    public IntegerToEnumConverter(Class<T> enumType) {
+        T[] enums = enumType.getEnumConstants();
+        for (T e : enums) {
+            enumMap.put(e.getCode(), e);
+        }
+    }
+
+    @Override
+    public T convert(Integer source) {
+        T t = enumMap.get(source);
+        if (t == null) {
+            throw new IllegalArgumentException("无法匹配对应的枚举类型");
+        }
+        return t;
+    }
+
+}

+ 14 - 6
src/main/java/com/zhixinghe1/ots/common/dto/JsonResponse.java

@@ -9,6 +9,7 @@ import java.io.Serializable;
 
 /**
  * 统一响应体
+ *
  * @param <T>
  */
 @Data
@@ -18,6 +19,8 @@ public class JsonResponse<T> implements Serializable {
     @Serial
     private static final long serialVersionUID = -5372948141725666083L;
 
+    private long timestamp = System.currentTimeMillis();
+
     /**
      * 是否成功标志位 true-成功 false-失败
      */
@@ -53,8 +56,9 @@ public class JsonResponse<T> implements Serializable {
 
     /**
      * 返回成功响应体
-     * @return
+     *
      * @param <T>
+     * @return
      */
     public static <T> JsonResponse<T> success() {
         return new JsonResponse<>(true, BaseErrorCodeEnum.SUCCESS.getCode(), BaseErrorCodeEnum.SUCCESS.getMsg());
@@ -62,9 +66,10 @@ public class JsonResponse<T> implements Serializable {
 
     /**
      * 返回成功响应体
+     *
      * @param data 响应数据
-     * @return
      * @param <T>
+     * @return
      */
     public static <T> JsonResponse<T> success(T data) {
         return new JsonResponse<>(true, BaseErrorCodeEnum.SUCCESS.getCode(), BaseErrorCodeEnum.SUCCESS.getMsg(), data);
@@ -72,8 +77,9 @@ public class JsonResponse<T> implements Serializable {
 
     /**
      * 返回错误响应体
-     * @return
+     *
      * @param <T>
+     * @return
      */
     public static <T> JsonResponse<T> fail() {
         return new JsonResponse<>(false, BaseErrorCodeEnum.FAIL.getCode(), BaseErrorCodeEnum.FAIL.getMsg());
@@ -81,9 +87,10 @@ public class JsonResponse<T> implements Serializable {
 
     /**
      * 返回错误响应体
+     *
      * @param msg 错误信息
-     * @return
      * @param <T>
+     * @return
      */
     public static <T> JsonResponse<T> fail(String msg) {
         return new JsonResponse<>(false, BaseErrorCodeEnum.FAIL.getCode(), msg);
@@ -91,10 +98,11 @@ public class JsonResponse<T> implements Serializable {
 
     /**
      * 返回错误响应体
+     *
      * @param code 错误编码
-     * @param msg 错误信息
-     * @return
+     * @param msg  错误信息
      * @param <T>
+     * @return
      */
     public static <T> JsonResponse<T> fail(String code, String msg) {
         return new JsonResponse<>(false, code, msg);

+ 14 - 0
src/main/java/com/zhixinghe1/ots/config/WebMvcConfig.java

@@ -0,0 +1,14 @@
+package com.zhixinghe1.ots.config;
+
+import com.zhixinghe1.ots.common.converter.IntegerCodeToEnumConverterFactory;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.format.FormatterRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class WebMvcConfig implements WebMvcConfigurer {
+    @Override
+    public void addFormatters(FormatterRegistry registry) {
+        registry.addConverterFactory(new IntegerCodeToEnumConverterFactory());
+    }
+}

+ 7 - 0
src/main/java/com/zhixinghe1/ots/constant/FontEnum.java

@@ -0,0 +1,7 @@
+package com.zhixinghe1.ots.constant;
+
+/**
+ * 字体
+ */
+public class FontEnum {
+}

+ 9 - 0
src/main/java/com/zhixinghe1/ots/constant/ImageTypeEnum.java

@@ -0,0 +1,9 @@
+package com.zhixinghe1.ots.constant;
+
+/**
+ * 图片类型
+ */
+public enum ImageTypeEnum {
+
+    JPEG, PNG, GIF, BMP, WBMP;
+}

+ 32 - 0
src/main/java/com/zhixinghe1/ots/constant/WaterMarkPositionEnum.java

@@ -0,0 +1,32 @@
+package com.zhixinghe1.ots.constant;
+
+import com.zhixinghe1.ots.common.converter.IJsonEnum;
+
+/**
+ * 水印位置枚举
+ *
+ * @author tyuio
+ */
+public enum WaterMarkPositionEnum implements IJsonEnum<Integer> {
+    TILE("平铺", 0),
+    CENTER("剧中", 1),
+    UPPER_LEFT("左上角", 2),
+    UPPER_RIGHT("右上角", 3),
+    LOWER_LEFT("左下角", 4),
+    LOWER_RIGHT("右下角", 5);
+
+    private final String name;
+
+    private final Integer code;
+
+    WaterMarkPositionEnum(String name, Integer code) {
+        this.name = name;
+        this.code = code;
+    }
+
+
+    @Override
+    public Integer getCode() {
+        return code;
+    }
+}

+ 24 - 0
src/main/java/com/zhixinghe1/ots/controller/WaterMarkController.java

@@ -0,0 +1,24 @@
+package com.zhixinghe1.ots.controller;
+
+import com.zhixinghe1.ots.dto.WaterMarkRequest;
+import com.zhixinghe1.ots.manager.IWaterMarkManager;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+@Tag(name = "水印")
+@RestController
+@RequestMapping("/watermark")
+public class WaterMarkController {
+
+    @Autowired
+    private IWaterMarkManager waterMarkManager;
+
+    @PostMapping(value = "/make", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+    public WaterMarkRequest make(@RequestPart MultipartFile file, @RequestBody WaterMarkRequest request) {
+//        waterMarkManager.make(file, request);
+        return null;
+    }
+}

+ 122 - 0
src/main/java/com/zhixinghe1/ots/core/TileImageFilter.java

@@ -0,0 +1,122 @@
+package com.zhixinghe1.ots.core;
+
+import net.coobird.thumbnailator.filters.ImageFilter;
+import net.coobird.thumbnailator.util.BufferedImages;
+
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+
+/**
+ * 水印平铺
+ */
+public class TileImageFilter implements ImageFilter {
+
+    /**
+     * 水印字体
+     */
+    private Font font;
+
+    /**
+     * 水印字体颜色
+     */
+    private Color c;
+
+    /**
+     * 水印字体透明度
+     */
+    private Float alpha;
+
+    /**
+     * 水印内容
+     */
+    private String caption;
+
+    /**
+     * 水印文字旋转角度
+     */
+    private Integer degree;
+
+    /**
+     * 平铺时X轴间隔
+     */
+    private Integer widthInterval;
+
+    /**
+     * 平铺时Y轴间隔
+     */
+    private Integer heightInterval;
+
+    /**
+     * 是否错开
+     */
+    private Boolean criscross;
+
+    public TileImageFilter(Font font, Color c, Float alpha, String caption, Integer degree, Integer widthInterval, Integer heightInterval, Boolean criscross) {
+        this.font = font;
+        this.c = c;
+        this.alpha = alpha;
+        this.caption = caption;
+        this.degree = degree;
+        this.widthInterval = widthInterval;
+        this.heightInterval = heightInterval;
+        this.criscross = criscross;
+    }
+
+    @Override
+    public BufferedImage apply(BufferedImage img) {
+        BufferedImage newImage = BufferedImages.copy(img);
+
+        Graphics2D g = newImage.createGraphics();
+        g.setFont(font);
+        g.setColor(c);
+        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
+
+        // 图片长度、图片宽度
+        int imageWidth = img.getWidth();
+        int imageHeight = img.getHeight();
+
+        // 图片中心坐标
+        int centerX = imageWidth / 2;
+        int centerY = imageHeight / 2;
+
+        // 内容长度、内容高度
+        int captionWidth = g.getFontMetrics().stringWidth(caption);
+        int captionHeight = g.getFontMetrics().getHeight();
+
+        // 内容间隔
+        int tmpWidthInterval = captionWidth + widthInterval;
+        int tmpHeightInterval = captionHeight + heightInterval;
+
+        // 错开时偏移量
+        int offsetX = criscross ? tmpWidthInterval / 2 : 0;
+
+        // 文字旋转
+        g.transform(AffineTransform.getRotateInstance(Math.toRadians(degree), 0, 0));
+
+        // 计算字体坐标
+        for (int i = centerX; i < 2 * imageWidth; i = i + tmpWidthInterval) {
+            for (int j = centerY, idx = 0; j < 2 * imageHeight; j = j + tmpHeightInterval, idx++) {
+                g.drawString(caption, idx % 2 == 0 ? offsetX + i : i, j);
+            }
+
+            for (int j = centerY - tmpHeightInterval, idx = 1; j > 2 * (-imageHeight); j = j - tmpHeightInterval, idx++) {
+                g.drawString(caption, idx % 2 == 0 ? offsetX + i : i, j);
+            }
+        }
+
+        for (int i = centerX - tmpWidthInterval; i > 2 * (-imageWidth); i = i - tmpWidthInterval) {
+            for (int j = centerY, idx = 0; j < 2 * imageHeight; j = j + tmpHeightInterval, idx++) {
+                g.drawString(caption, idx % 2 == 0 ? offsetX + i : i, j);
+            }
+
+            for (int j = centerY - tmpHeightInterval, idx = 1; j > 2 * (-imageHeight); j = j - tmpHeightInterval, idx++) {
+                g.drawString(caption, idx % 2 == 0 ? offsetX + i : i, j);
+            }
+        }
+
+        g.dispose();
+
+        return newImage;
+    }
+}

+ 11 - 0
src/main/java/com/zhixinghe1/ots/dto/WaterMarkFormat.java

@@ -0,0 +1,11 @@
+package com.zhixinghe1.ots.dto;
+
+import lombok.Data;
+
+@Data
+public class WaterMarkFormat {
+
+    private String type;
+
+    private String quality;
+}

+ 16 - 0
src/main/java/com/zhixinghe1/ots/dto/WaterMarkLayout.java

@@ -0,0 +1,16 @@
+package com.zhixinghe1.ots.dto;
+
+import com.zhixinghe1.ots.constant.WaterMarkPositionEnum;
+import lombok.Data;
+
+@Data
+public class WaterMarkLayout {
+
+    private WaterMarkPositionEnum position;
+
+    private Boolean criscross;
+
+    private Integer degree;
+
+    private Integer interval;
+}

+ 15 - 0
src/main/java/com/zhixinghe1/ots/dto/WaterMarkRequest.java

@@ -0,0 +1,15 @@
+package com.zhixinghe1.ots.dto;
+
+import lombok.Data;
+
+@Data
+public class WaterMarkRequest {
+
+    private String content;
+
+    private WaterMarkLayout waterMarkLayout;
+
+    private WaterMarkStyle waterMarkStyle;
+
+    private WaterMarkFormat waterMarkFormat;
+}

+ 20 - 0
src/main/java/com/zhixinghe1/ots/dto/WaterMarkStyle.java

@@ -0,0 +1,20 @@
+package com.zhixinghe1.ots.dto;
+
+import lombok.Data;
+
+@Data
+public class WaterMarkStyle {
+
+    private Integer size;
+
+    private Boolean bold;
+
+    private String color;
+
+    /**
+     * 1-100
+     */
+    private Integer alpha;
+
+    private String font;
+}

+ 10 - 0
src/main/java/com/zhixinghe1/ots/manager/IWaterMarkManager.java

@@ -0,0 +1,10 @@
+package com.zhixinghe1.ots.manager;
+
+import com.zhixinghe1.ots.dto.WaterMarkRequest;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+public interface IWaterMarkManager {
+
+    void make(HttpServletResponse response, WaterMarkRequest request2);
+}

+ 39 - 0
src/main/java/com/zhixinghe1/ots/manager/WaterMarkManagerImpl.java

@@ -0,0 +1,39 @@
+package com.zhixinghe1.ots.manager;
+
+import com.zhixinghe1.ots.dto.WaterMarkRequest;
+import com.zhixinghe1.ots.dto.WaterMarkStyle;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import net.coobird.thumbnailator.filters.Caption;
+import net.coobird.thumbnailator.geometry.Positions;
+import org.springframework.stereotype.Service;
+
+import java.awt.*;
+
+@Slf4j
+@Service
+public class WaterMarkManagerImpl implements IWaterMarkManager {
+
+    @Override
+    public void make(HttpServletResponse response, WaterMarkRequest request2) {
+//        WaterMarkLayout waterMarkLayout = request.getWaterMarkLayout();
+//        WaterMarkStyle waterMarkStyle = request.getWaterMarkStyle();
+//        WaterMarkFormat waterMarkFormat = request.getWaterMarkFormat();
+//        try {
+//            Thumbnails.of(file.getInputStream()).toOutputStream(response.getOutputStream())
+//                .scale(1, 1)
+//                .addFilter(createCaption(request))
+//                    .toFile("C:\\Users\\tyuio\\Desktop\\捕获2.PNG");
+//        } catch (IOException e) {
+//            log.error("添加水印时发生异常", e);
+//        }
+
+    }
+
+    private Caption createCaption(WaterMarkRequest request) {
+        WaterMarkStyle style = request.getWaterMarkStyle();
+        Font font = new Font(style.getFont(), Font.BOLD, style.getSize());
+        return new Caption(request.getContent(), font, Color.BLUE, style.getAlpha(), Positions.BOTTOM_RIGHT, 5);
+    }
+
+}