Java服务端使用image-combiner生成微信小程序海报

1. 首先导入image-combiner的maven依赖

  <!--图片合成-->
  <dependency>
      <groupId>com.freewayso</groupId>
      <artifactId>image-combiner</artifactId>
      <version>2.6.9</version>
  </dependency>

2. 由于业务需要,生成海报里要课程码,因此我这边使用的是微信官方提供的生成微信小程序码的api

  Map<String, Object> param = new HashMap<>();
  param.put("page", "pages/loginModule/register/register");
  param.put("scene", "invitationCode=" + invitationCode + "&courseId=" + courseId);
  param.put("width", 430);
  param.put("check_path", false);
  param.put("env_version", "develop");
  String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + accessToken;
  String jsonParams = JSONUtil.toJsonStr(param);
  byte[] responseBytes = HttpUtil.createPost(url)
        .header("Content-Type", "application/json")
        .body(jsonParams)
        .execute()
        .bodyBytes();

通过Hutool工具类中的HttpUtil.createPost(url)得到微信小程序码的图片字节数组

将字节数组包装成输入流,并通过ImageIO.read读取生成图像BufferedImage
BufferedImage qrImage = ImageIO.read(new ByteArrayInputStream(responseBytes));

分别读取空白图像和课程图片的网络输入流,并生成图像BufferedImage

  // 加载一张空白的背景图combiner
  String blankUrl = "https://img.picui.cn/free/2025/06/21/6856a072e9eea.png";
  ImageCombiner combiner = new ImageCombiner(blankUrl, 341, 391, ZoomMode.WidthHeight, OutputFormat.PNG);

  // 加载课程图片
  String courseUrl = SystemConstant.FILE_COMMON_PREFIX + course.getImage();
  BufferedImage courseImage = ImageIO.read(new URL(courseUrl));

缩放二维码图片(消除锯齿,不失真)

  Image scaledImage = qrImage.getScaledInstance(134, 134, Image.SCALE_SMOOTH);
  BufferedImage resizedImage = new BufferedImage(134, 134, BufferedImage.TYPE_INT_RGB);
  resizedImage.getGraphics().drawImage(scaledImage, 0, 0, null);

3.将课程图片和二维码图片添加到背景图combiner中

  // 将课程图片添加到组合器中
  combiner.addImageElement(courseImage, 18, 16, 306, 158, ZoomMode.WidthHeight).setRoundCorner(5).setCenter(true);
  // 将二维码图片添加到组合器combiner中
  combiner.addImageElement(resizedImage, 190, 240);

4.将文本添加到背景图combiner中

  // 绘制文本
  TextElement courseTitleElement = new TextElement(courseTitle, FontType, FontStyle, 18, 20, 186);
  courseTitleElement.setColor(Color.decode("#323232"))
                      .setCenter(true)
                      .setAutoBreakLine(306);
  combiner.addElement(courseTitleElement);

  TextElement originalPriceElement = new TextElement(originalPrice, FontType, FontStyle, 24, 56, 275);
  originalPriceElement.setColor(Color.decode("#8C8C8C")).setStrikeThrough(true);
  combiner.addElement(originalPriceElement);

  TextElement discountedPriceElement = new TextElement(discountPrice, FontType, Font.BOLD, 28, 31, 343);
  discountedPriceElement.setColor(Color.decode("#F84947")).setBaseLine(BaseLine.Base).setSpace(0.01f);
  combiner.addElement(discountedPriceElement);

  TextElement discountedTextElement = new TextElement(discountText, FontType, FontStyle, 16, discountedPriceElement.getX() + discountedPriceElement.getWidth(), 341);
  discountedTextElement.setColor(Color.decode("#F84947")).setBaseLine(BaseLine.Base);
  combiner.addElement(discountedTextElement);

5.将添加的图片和文本合并到背景图combiner中
combiner.combine();

6.获取最终组合器combiner的字节数组,并将文件上传到指定位置

  InputStream resultStream = combiner.getCombinedImageStream();
  byte[] resultBytes = resultStream.readAllBytes();
  // 创建上传目录,如果不存在
  String biz = "default";
  String fileName = RandomStringUtils.random(8, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + "." + "png";
  // 获取文件类型
  String fileType = FileUtil.getSuffix(fileName);
  // 获取view值
  String view = RandomStringUtils.random(8, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
  File file = new File(uploadDir + fileName);
  if (!file.getParentFile().exists()) {
      file.getParentFile().mkdirs();// 如果路径不存在则创建
  }
  // 将文件上传到目标位置
  try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file), BUFFER_SIZE)) {
      bos.write(resultBytes);
  } catch (IOException e) {
      throw new BusinessException(ErrorCode.OPERATION_ERROR, "文件上传失败,失败原因:" + e.getMessage());
  }

完整代码

/**
     * 微信小程序获取课程码
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String getWxCourseQrCode(CommonRequest commonRequest, HttpServletRequest request) throws Exception {
        String accessToken = (String) redisTemplate.opsForValue().get(ACCESS_TOKEN_KEY);
        if (accessToken == null) {
            accessToken = this.getAccessToken().getAccess_token();
        }
        // 获取用户邀请码
        Long userId = (Long) request.getAttribute("userId");
        UserInfo userInfo = userInfoMapper.selectById(userId);
        String invitationCode = userInfo.getInvitationCode();
        // 获取课程信息
        Long courseId = commonRequest.getId();
        Course course = courseService.getById(courseId);
        String courseTitle = course.getName();
        String originalPrice = String.valueOf(course.getOriginPrice());
        String discountPrice = String.valueOf(course.getDiscountPrice());
        String discountText = "元券后价";


        Map<String, Object> param = new HashMap<>();
        param.put("page", "pages/loginModule/register/register");
        param.put("scene", "invitationCode=" + invitationCode + "&courseId=" + courseId);
        param.put("width", 430);
        param.put("check_path", false);
        param.put("env_version", "develop");
        String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + accessToken;
        String jsonParams = JSONUtil.toJsonStr(param);
        byte[] responseBytes = HttpUtil.createPost(url)
                .header("Content-Type", "application/json")
                .body(jsonParams)
                .execute()
                .bodyBytes();

        String FontType = "Microsoft YaHei UI";
        int FontStyle = Font.PLAIN;

        // 加载一张空白图像
        String blankUrl = "https://img.picui.cn/free/2025/06/21/6856a072e9eea.png";
        ImageCombiner combiner = new ImageCombiner(blankUrl, 341, 391, ZoomMode.WidthHeight, OutputFormat.PNG);

        // 加载课程图片
        String courseUrl = SystemConstant.FILE_COMMON_PREFIX + course.getImage();
        BufferedImage courseImage = ImageIO.read(new URL(courseUrl));

        // 将二维码数据转换为 BufferedImage
        BufferedImage qrImage = ImageIO.read(new ByteArrayInputStream(responseBytes));

        // 缩放图片(不失真)
        Image scaledImage = qrImage.getScaledInstance(134, 134, Image.SCALE_SMOOTH);
        BufferedImage resizedImage = new BufferedImage(134, 134, BufferedImage.TYPE_INT_RGB);
        resizedImage.getGraphics().drawImage(scaledImage, 0, 0, null);

        combiner.setQuality(1f);

        // 将课程图片合并到组合器中
        combiner.addImageElement(courseImage, 18, 16, 306, 158, ZoomMode.WidthHeight).setRoundCorner(5).setCenter(true);
        // 将二维码图片合并到组合器中
        combiner.addImageElement(resizedImage, 190, 240);

        // 绘制文本
        TextElement courseTitleElement = new TextElement(courseTitle, FontType, FontStyle, 18, 20, 186);
        courseTitleElement.setColor(Color.decode("#323232"))
                            .setCenter(true)
                            .setAutoBreakLine(306);
        combiner.addElement(courseTitleElement);

        TextElement originalPriceElement = new TextElement(originalPrice, FontType, FontStyle, 24, 56, 275);
        originalPriceElement.setColor(Color.decode("#8C8C8C")).setStrikeThrough(true);
        combiner.addElement(originalPriceElement);

        TextElement discountedPriceElement = new TextElement(discountPrice, FontType, Font.BOLD, 28, 31, 343);
        discountedPriceElement.setColor(Color.decode("#F84947")).setBaseLine(BaseLine.Base).setSpace(0.01f);
        combiner.addElement(discountedPriceElement);

        TextElement discountedTextElement = new TextElement(discountText, FontType, FontStyle, 16, discountedPriceElement.getX() + discountedPriceElement.getWidth(), 341);
        discountedTextElement.setColor(Color.decode("#F84947")).setBaseLine(BaseLine.Base);
        combiner.addElement(discountedTextElement);

        combiner.combine();

        InputStream resultStream = combiner.getCombinedImageStream();
        byte[] resultBytes = resultStream.readAllBytes();
        // 创建上传目录,如果不存在
        String biz = "default";
        String fileName = RandomStringUtils.random(8, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + "." + "png";
        // 获取文件类型
        String fileType = FileUtil.getSuffix(fileName);
        // 获取view值
        String view = RandomStringUtils.random(8, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
        File file = new File(uploadDir + fileName);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();// 如果路径不存在则创建
        }
        // 将文件上传到目标位置
        try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file), BUFFER_SIZE)) {
            bos.write(resultBytes);
        } catch (IOException e) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "文件上传失败,失败原因:" + e.getMessage());
        }

        // 获取文件大小
        Double fileSize = file.length() / 1024.0;
        fileSize = Double.valueOf(String.format("%.2f", fileSize));
        // 获取文件哈希值
        InputStream inputStream = new FileInputStream(file);
        String hashValue = DigestUtils.sha256Hex(inputStream);
        // 保存文件
        FileInfo fileInfo = FileInfo.builder()
                .name(fileName)
                .type(fileType)
                .size(fileSize)
                .fileView(view)
                .biz(biz)
                .hashValue(hashValue)
                .build();
        fileInfoService.save(fileInfo);

        String viewValue = biz + "-" + view;
        // 保存课程推广码申请记录
        CourseQrcodeApply courseQrcodeApply = CourseQrcodeApply.builder()
                .userId(userId)
                .courseId(courseId)
                .courseQrcode(viewValue)
                .build();
        courseQrcodeApplyService.save(courseQrcodeApply);
        return viewValue;
    }

最终生成效果图
XEPDIBZ6

posted @ 2025-06-28 22:39  chenxinzhi  阅读(47)  评论(0)    收藏  举报