Browse Source

生成个人头像和群头像

imndx 2 years ago
parent
commit
f3a7fc9719

+ 1 - 0
.gitignore

@@ -3,3 +3,4 @@ target
 appdata.mv.db
 nohup.out
 appdata.trace.db
+avatar/

+ 3 - 0
config/application.properties

@@ -106,3 +106,6 @@ spring.mail.properties.mail.imap.ssl.socketFactory.fallback=false
 
 # 邮箱必须是有效邮箱,如果是无效邮箱可能会发送失败
 spring.mail.to_lists=admin1@wildfirechat.cn,admin2@wildfirechat.cn,admin3@wildfirechat.cn
+
+# 头像背景颜色可选列表,逗号分隔,中间不能有空格
+avatar.bg.corlors=#D1C4E9,#B39DDB,#9575CD,#7E57C2,#673AB7,#5E35B1,#512DA8,#4527A0,#311B92,#B388FF,#7C4DFF,#651FFF,#6200EA,#F3E5F5,#E1BEE7

+ 30 - 0
src/main/java/cn/wildfirechat/app/avatar/AvatarController.java

@@ -0,0 +1,30 @@
+package cn.wildfirechat.app.avatar;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.openjsse.net.ssl.OpenJSSE;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+@RestController
+@RequestMapping(value = "/avatar")
+public class AvatarController {
+    @Autowired
+    AvatarService avatarService;
+
+    @CrossOrigin
+    @GetMapping()
+    public ResponseEntity<byte[]> avatar(@RequestParam("name") String name) throws IOException {
+        return avatarService.avatar(name);
+    }
+
+    @GetMapping("/group")
+    public ResponseEntity<byte[]> groupAvatar(@RequestParam("request") String request) throws IOException, URISyntaxException {
+        ObjectMapper mapper = new ObjectMapper();
+        GroupAvatarRequest groupAvatarRequest = mapper.readValue(request, GroupAvatarRequest.class);
+        return avatarService.groupAvatar(groupAvatarRequest);
+    }
+}

+ 12 - 0
src/main/java/cn/wildfirechat/app/avatar/AvatarService.java

@@ -0,0 +1,12 @@
+package cn.wildfirechat.app.avatar;
+
+import org.springframework.http.ResponseEntity;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+public interface AvatarService {
+    ResponseEntity<byte[]> avatar(String name) throws IOException;
+
+    ResponseEntity<byte[]> groupAvatar(GroupAvatarRequest requeset) throws IOException, URISyntaxException;
+}

+ 82 - 0
src/main/java/cn/wildfirechat/app/avatar/AvatarServiceImpl.java

@@ -0,0 +1,82 @@
+package cn.wildfirechat.app.avatar;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StreamUtils;
+import org.springframework.util.StringUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+public class AvatarServiceImpl implements AvatarService {
+    @Value("${avatar.bg.corlors}")
+    String bgColors;
+
+    @Override
+    public ResponseEntity<byte[]> avatar(String name) throws IOException {
+        File file = nameAvatar(name);
+        if (file.exists()) {
+            byte[] bytes = StreamUtils.copyToByteArray(Files.newInputStream(file.toPath()));
+            return ResponseEntity
+                .ok()
+                .contentType(MediaType.IMAGE_PNG)
+                .body(bytes);
+        } else {
+            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
+        }
+    }
+
+    @Override
+    public ResponseEntity<byte[]> groupAvatar(GroupAvatarRequest request) throws IOException, URISyntaxException {
+        List<GroupAvatarRequest.GroupMemberInfo> infos = request.getMembers();
+        List<URL> paths = new ArrayList<>();
+        long hashCode = 0;
+        for (int i = 0; i < infos.size() && i < 9; i++) {
+            GroupAvatarRequest.GroupMemberInfo info = infos.get(i);
+            if (!StringUtils.isEmpty(info.getAvatarUrl())) {
+                paths.add(new URL(info.getAvatarUrl()));
+                hashCode += info.getAvatarUrl().hashCode();
+            } else {
+                File file = nameAvatar(info.getName());
+                paths.add(file.toURI().toURL());
+                hashCode += info.getName().hashCode();
+            }
+        }
+        File file = new File("./avatar/" + hashCode + "-group.png");
+        if (!file.exists()) {
+            GroupAvatarUtil.getCombinationOfHead(paths, file);
+        }
+
+        if (file.exists()) {
+            byte[] bytes = StreamUtils.copyToByteArray(Files.newInputStream(file.toPath()));
+            return ResponseEntity
+                .ok()
+                .contentType(MediaType.IMAGE_PNG)
+                .body(bytes);
+        } else {
+            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
+        }
+    }
+
+    private File nameAvatar(String name) {
+        String[] colors = bgColors.split(",");
+        int len = colors.length;
+        int hashCode = name.hashCode();
+        File file = new File("./avatar/" + hashCode + ".png");
+        if (!file.exists()) {
+            String color = colors[Math.abs(name.hashCode() % len)];
+            // 最后一个字符
+            file = new NameAvatarBuilder(color).name(name.substring(name.length() - 1), name).build();
+        }
+        return file;
+    }
+}

+ 36 - 0
src/main/java/cn/wildfirechat/app/avatar/GroupAvatarRequest.java

@@ -0,0 +1,36 @@
+package cn.wildfirechat.app.avatar;
+
+import java.util.List;
+
+public class GroupAvatarRequest {
+    private List<GroupMemberInfo> members;
+
+    public List<GroupMemberInfo> getMembers() {
+        return members;
+    }
+
+    public void setMembers(List<GroupMemberInfo> members) {
+        this.members = members;
+    }
+
+    public static class GroupMemberInfo {
+        private String name;
+        private String avatarUrl;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getAvatarUrl() {
+            return avatarUrl;
+        }
+
+        public void setAvatarUrl(String avatarUrl) {
+            this.avatarUrl = avatarUrl;
+        }
+    }
+}

+ 198 - 0
src/main/java/cn/wildfirechat/app/avatar/GroupAvatarUtil.java

@@ -0,0 +1,198 @@
+package cn.wildfirechat.app.avatar;
+
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+import java.awt.image.AffineTransformOp;
+import java.awt.image.BufferedImage;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author: fangdaji
+ * @date: 2019/3/23 15:59
+ * @description:
+ */
+public class GroupAvatarUtil {
+    public static void getCombinationOfHead(List<URL> paths, File targetFile)
+        throws IOException, URISyntaxException {
+
+        List<BufferedImage> bufferedImages = new ArrayList<BufferedImage>();
+        // 压缩图片所有的图片生成尺寸同意的 为 50x50
+
+        int imageSize = 33;
+        if (paths.size() <= 4) {
+            imageSize = 50;
+        }
+
+        for (int i = 0; i < paths.size(); i++) {
+            bufferedImages.add(resize2(paths.get(i), imageSize, imageSize, true));
+        }
+
+        int width = 112; // 这是画板的宽高
+
+        int height = 112; // 这是画板的高度
+
+        // BufferedImage.TYPE_INT_RGB可以自己定义可查看API
+
+        BufferedImage outImage = new BufferedImage(width, height,
+            BufferedImage.TYPE_INT_RGB);
+
+        // 生成画布
+        Graphics g = outImage.getGraphics();
+
+        Graphics2D g2d = (Graphics2D) g;
+
+        // 设置背景色
+        g2d.setBackground(new Color(231, 231, 231));
+        //g2d.setBackground(new Color(231, 0, 4));
+
+        // 通过使用当前绘图表面的背景色进行填充来清除指定的矩形。
+        g2d.clearRect(0, 0, width, height);
+
+        // 开始拼凑 根据图片的数量判断该生成那种样式的组合头像目前为4中
+        int j = 1;
+        int k = 1;
+        for (int i = 1; i <= bufferedImages.size(); i++) {
+            if (bufferedImages.size() == 9) {
+                if (i <= 3) {
+                    g2d.drawImage(bufferedImages.get(i - 1), 33 * i + 3 * i - 33, 4, null);
+                } else if (i <= 6) {
+                    g2d.drawImage(bufferedImages.get(i - 1), 33 * j + 3 * j - 33, 41, null);
+                    j++;
+                } else {
+                    g2d.drawImage(bufferedImages.get(i - 1), 33 * k + 3 * k - 33, 77, null);
+                    k++;
+                }
+            } else if (bufferedImages.size() == 8) {
+                if (i <= 2) {
+                    g2d.drawImage(bufferedImages.get(i - 1), 33 * i + 4 * i - 18, 4, null);
+                } else if (i <= 5) {
+                    g2d.drawImage(bufferedImages.get(i - 1), 33 * j + 3 * j - 33, 41, null);
+                    j++;
+                } else {
+                    g2d.drawImage(bufferedImages.get(i - 1), 33 * k + 3 * k - 33, 77, null);
+                    k++;
+                }
+            } else if (bufferedImages.size() == 7) {
+                if (i <= 1) {
+                    g2d.drawImage(bufferedImages.get(i - 1), 39, 4, null);
+                } else if (i <= 4) {
+                    g2d.drawImage(bufferedImages.get(i - 1), 33 * j + 3 * j - 33, 41, null);
+                    j++;
+                } else {
+                    g2d.drawImage(bufferedImages.get(i - 1), 33 * k + 3 * k - 33, 77, null);
+                    k++;
+                }
+            } else if (bufferedImages.size() == 6) {
+                if (i <= 3) {
+                    g2d.drawImage(bufferedImages.get(i - 1), 33 * i + 3 * i - 33, 15, null);
+                } else {
+                    g2d.drawImage(bufferedImages.get(i - 1), 33 * j + 3 * j - 33, 58, null);
+                    j++;
+                }
+            } else if (bufferedImages.size() == 5) {
+                if (i <= 2) {
+                    g2d.drawImage(bufferedImages.get(i - 1), 33 * i + 4 * i - 18, 15, null);
+                } else {
+                    g2d.drawImage(bufferedImages.get(i - 1), 33 * j + 3 * j - 33, 58, null);
+                    j++;
+                }
+            } else if (bufferedImages.size() == 4) {
+                if (i <= 2) {
+                    g2d.drawImage(bufferedImages.get(i - 1), 50 * i + 4 * i - 50, 4, null);
+                } else {
+                    g2d.drawImage(bufferedImages.get(i - 1), 50 * j + 4 * j - 50, 58, null);
+                    j++;
+                }
+            } else if (bufferedImages.size() == 3) {
+                if (i <= 1) {
+                    g2d.drawImage(bufferedImages.get(i - 1), 31, 4, null);
+                } else {
+                    g2d.drawImage(bufferedImages.get(i - 1), 50 * j + 4 * j - 50, 58, null);
+                    j++;
+                }
+
+            } else if (bufferedImages.size() == 2) {
+
+                g2d.drawImage(bufferedImages.get(i - 1), 50 * i + 4 * i - 50,
+                    31, null);
+
+            } else if (bufferedImages.size() == 1) {
+
+                g2d.drawImage(bufferedImages.get(i - 1), 31, 31, null);
+
+            }
+
+            // 需要改变颜色的话在这里绘上颜色。可能会用到AlphaComposite类
+        }
+
+
+        String format = "png";
+        ImageIO.write(outImage, format, targetFile);
+    }
+
+    /**
+     * 图片缩放
+     *
+     * @param filePath 图片路径
+     * @param height   高度
+     * @param width    宽度
+     * @param bb       比例不对时是否需要补白
+     */
+    private static BufferedImage resize2(URL filePath, int height, int width,
+                                         boolean bb) throws URISyntaxException {
+        try {
+            double ratio = 0; // 缩放比例
+
+            DataInputStream dis = new DataInputStream(filePath.openStream());
+
+            //File f = new File(dis);
+            BufferedImage bi = ImageIO.read(dis);
+            Image itemp = bi.getScaledInstance(width, height,
+                Image.SCALE_SMOOTH);
+            // 计算比例
+            if ((bi.getHeight() > height) || (bi.getWidth() > width)) {
+                if (bi.getHeight() > bi.getWidth()) {
+                    ratio = (new Integer(height)).doubleValue()
+                        / bi.getHeight();
+                } else {
+                    ratio = (new Integer(width)).doubleValue() / bi.getWidth();
+                }
+                AffineTransformOp op = new AffineTransformOp(
+                    AffineTransform.getScaleInstance(ratio, ratio), null);
+                itemp = op.filter(bi, null);
+            }
+            if (bb) {
+                // copyimg(filePath, "D:\\img");
+                BufferedImage image = new BufferedImage(width, height,
+                    BufferedImage.TYPE_INT_RGB);
+                Graphics2D g = image.createGraphics();
+                g.setColor(Color.white);
+                g.fillRect(0, 0, width, height);
+                if (width == itemp.getWidth(null)) {
+                    g.drawImage(itemp, 0, (height - itemp.getHeight(null)) / 2,
+                        itemp.getWidth(null), itemp.getHeight(null),
+                        Color.white, null);
+                } else {
+                    g.drawImage(itemp, (width - itemp.getWidth(null)) / 2, 0,
+                        itemp.getWidth(null), itemp.getHeight(null),
+                        Color.white, null);
+                }
+                g.dispose();
+                itemp = image;
+            }
+            return (BufferedImage) itemp;
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+}

+ 58 - 0
src/main/java/cn/wildfirechat/app/avatar/NameAvatarBuilder.java

@@ -0,0 +1,58 @@
+package cn.wildfirechat.app.avatar;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+public class NameAvatarBuilder {
+
+    private BufferedImage templateImage;
+    private Graphics2D templateG2D;
+    private int templateWidth;
+    private int templateHeight;
+
+    private String fullName;
+
+    public NameAvatarBuilder(String bgRGB) {
+        //            templateImage = ImageIO.read(new File("./avatar/headbg.jpg"));
+        templateImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
+        templateG2D = templateImage.createGraphics();
+        templateWidth = templateImage.getWidth();
+        templateHeight = templateImage.getHeight();
+        templateG2D.setBackground(Color.decode(bgRGB));
+        templateG2D.clearRect(0, 0, templateWidth, templateHeight);
+    }
+
+    public NameAvatarBuilder name(String drawName, String fullName) {
+        this.fullName = fullName;
+        int x = templateWidth * 3 / 10;
+        int y = templateHeight * 2 / 3;
+        // templateG2D.translate(0, 100);
+        // g2.rotate(Math.PI / 3);
+        templateG2D.setFont(templateG2D.getFont().deriveFont(40f));
+        templateG2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+            RenderingHints.VALUE_ANTIALIAS_ON);
+        // templateG2D.setColor(Color.WHITE);
+        templateG2D.setColor(Color.decode("#FFFFFF"));
+        templateG2D.drawString(drawName, x, y);
+        return this;
+    }
+
+    public File build() {
+        templateG2D.dispose();
+        templateImage.flush();
+        File file = new File("./avatar/" + this.fullName.hashCode() + ".png");
+        try {
+            ImageIO.write(templateImage, "png", file);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        // System.gc();
+        return file;
+    }
+}

+ 3 - 1
src/main/java/cn/wildfirechat/app/shiro/ShiroConfig.java

@@ -48,7 +48,6 @@ public class ShiroConfig {
         filterChainDefinitionMap.put("/send_code", "anon");
         filterChainDefinitionMap.put("/login", "anon");
         filterChainDefinitionMap.put("/pc_session", "anon");
-        filterChainDefinitionMap.put("/amr2mp3", "anon");
 
         filterChainDefinitionMap.put("/login_pwd", "anon");
         filterChainDefinitionMap.put("/send_reset_code", "anon");
@@ -69,6 +68,9 @@ public class ShiroConfig {
         filterChainDefinitionMap.put("/things/add_device", "login");
         filterChainDefinitionMap.put("/things/list_device", "login");
 
+        filterChainDefinitionMap.put("/amr2mp3", "login");
+        filterChainDefinitionMap.put("/avatar/**", "login");
+
         //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证
         filterChainDefinitionMap.put("/**", "login");
         shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);